diff --git a/.travis.yml b/.travis.yml index 66f91d8b..8050c10d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,6 @@ script: cd actix_todo && cargo check && cd .. cd basics && cargo check && cd .. cd cookie-auth && cargo check && cd .. - cd cookie-auth-full && cargo check && cd .. cd cookie-session && cargo check && cd .. cd diesel && cargo check && cd .. cd error_handling && cargo check && cd .. @@ -55,11 +54,11 @@ script: cd protobuf && cargo check && cd .. cd r2d2 && cargo check && cd .. cd redis-session && cargo check && cd .. - cd simple-auth-sarver && cargo check && cd .. cd state && cargo check && cd .. cd static_index && cargo check && cd .. cd template_askama && cargo check && cd .. cd template_tera && cargo check && cd .. + cd template_yarte && cargo check && cd .. cd tls && cargo check && cd .. cd rustls && cargo check && cd .. cd unix-socket && cargo check && cd .. diff --git a/Cargo.toml b/Cargo.toml index ae0b605e..7422c22d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ members = [ "async_ex1", "basics", "cookie-auth", - "cookie-auth-full", "cookie-session", "diesel", "error_handling", @@ -27,6 +26,7 @@ members = [ "static_index", "template_askama", "template_tera", + "template_yarte", "tls", "rustls", "unix-socket", diff --git a/actix_redis/Cargo.toml b/actix_redis/Cargo.toml index a716e624..b8014247 100644 --- a/actix_redis/Cargo.toml +++ b/actix_redis/Cargo.toml @@ -2,13 +2,15 @@ name = "actix_redis" version = "0.1.0" authors = ["dowwie "] +edition = "2018" +workspace = ".." [dependencies] -actix = "0.7.3" -actix-web = "0.7.3" -actix-redis = "0.5.1" +actix = "0.8.0-alpha.2" +actix-web = "1.0.0-alpha.1" +actix-redis = { git="https://github.com/actix/actix-redis.git" } futures = "0.1.23" redis-async = "0.4.0" serde = "1.0.71" serde_derive = "1.0.71" -env_logger = "0.5.12" +env_logger = "0.6" diff --git a/actix_redis/src/main.rs b/actix_redis/src/main.rs index d9ccfa8e..a9ed3c4c 100644 --- a/actix_redis/src/main.rs +++ b/actix_redis/src/main.rs @@ -1,34 +1,26 @@ -extern crate actix; -extern crate actix_redis; -extern crate actix_web; -extern crate env_logger; -extern crate futures; -#[macro_use] extern crate redis_async; -extern crate serde; -#[macro_use] extern crate serde_derive; +#[macro_use] +extern crate redis_async; +#[macro_use] +extern crate serde_derive; - -use std::sync::Arc; use actix::prelude::*; -use actix_redis::{Command, RedisActor, Error as ARError}; -use actix_web::{middleware, server, App, HttpRequest, HttpResponse, Json, - AsyncResponder, http::Method, Error as AWError}; -use futures::future::{Future, join_all}; +use actix_redis::{Command, Error as ARError, RedisActor}; +use actix_web::{middleware, web, App, Error as AWError, HttpResponse, HttpServer}; +use futures::future::{join_all, Future}; use redis_async::resp::RespValue; - #[derive(Deserialize)] pub struct CacheInfo { one: String, two: String, - three: String + three: String, } - -fn cache_stuff((info, req): (Json, HttpRequest)) - -> impl Future { +fn cache_stuff( + info: web::Json, + redis: web::Data>, +) -> impl Future { let info = info.into_inner(); - let redis = req.state().redis_addr.clone(); let one = redis.send(Command(resp_array!["SET", "mydomain:one", info.one])); let two = redis.send(Command(resp_array!["SET", "mydomain:two", info.two])); @@ -44,63 +36,58 @@ fn cache_stuff((info, req): (Json, HttpRequest)) let info_set = join_all(vec![one, two, three].into_iter()); info_set - .map_err(AWError::from) - .and_then(|res: Vec>| - // successful operations return "OK", so confirm that all returned as so - if !res.iter().all(|res| match res { - Ok(RespValue::SimpleString(x)) if x=="OK" => true, - _ => false - }) { - Ok(HttpResponse::InternalServerError().finish()) - } else { - Ok(HttpResponse::Ok().body("successfully cached values")) - } - ) - .responder() + .map_err(AWError::from) + .and_then(|res: Vec>| + // successful operations return "OK", so confirm that all returned as so + if !res.iter().all(|res| match res { + Ok(RespValue::SimpleString(x)) if x=="OK" => true, + _ => false + }) { + Ok(HttpResponse::InternalServerError().finish()) + } else { + Ok(HttpResponse::Ok().body("successfully cached values")) + } + ) } -fn del_stuff(req: HttpRequest) - -> impl Future { - let redis = req.state().redis_addr.clone(); - - redis.send(Command(resp_array!["DEL", "mydomain:one", "mydomain:two", "mydomain:three"])) - .map_err(AWError::from) - .and_then(|res: Result| - match &res { - Ok(RespValue::Integer(x)) if x==&3 => - Ok(HttpResponse::Ok().body("successfully deleted values")), - _ =>{println!("---->{:?}", res); - Ok(HttpResponse::InternalServerError().finish())} - }) - .responder() - +fn del_stuff( + redis: web::Data>, +) -> impl Future { + redis + .send(Command(resp_array![ + "DEL", + "mydomain:one", + "mydomain:two", + "mydomain:three" + ])) + .map_err(AWError::from) + .and_then(|res: Result| match &res { + Ok(RespValue::Integer(x)) if x == &3 => { + Ok(HttpResponse::Ok().body("successfully deleted values")) + } + _ => { + println!("---->{:?}", res); + Ok(HttpResponse::InternalServerError().finish()) + } + }) } -pub struct AppState { - pub redis_addr: Arc> -} - -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info,actix_redis=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info,actix_redis=info"); env_logger::init(); - let sys = actix::System::new("actix_redis_ex"); - server::new(|| { - let redis_addr = Arc::new(RedisActor::start("127.0.0.1:6379")); - let app_state = AppState{redis_addr}; + HttpServer::new(|| { + let redis_addr = RedisActor::start("127.0.0.1:6379"); - App::with_state(app_state) - .middleware(middleware::Logger::default()) - .resource("/stuff", |r| { - r.method(Method::POST) - .with_async(cache_stuff); - r.method(Method::DELETE) - .with_async(del_stuff)}) - - }).bind("0.0.0.0:8080") - .unwrap() - .workers(1) - .start(); - - let _ = sys.run(); + App::new() + .data(redis_addr) + .wrap(middleware::Logger::default()) + .service( + web::resource("/stuff") + .route(web::post().to_async(cache_stuff)) + .route(web::delete().to_async(del_stuff)), + ) + }) + .bind("0.0.0.0:8080")? + .run() } diff --git a/actix_todo/Cargo.toml b/actix_todo/Cargo.toml index eb17097e..17a35307 100644 --- a/actix_todo/Cargo.toml +++ b/actix_todo/Cargo.toml @@ -2,10 +2,13 @@ authors = ["Dan Munckton "] name = "actix-todo" version = "0.1.0" +workspace = ".." +edition = "2018" [dependencies] -actix = "0.7.3" -actix-web = "0.7.4" +actix-web = "1.0.0-alpha.1" +actix-files = "0.1.0-alpha.1" +actix-session = "0.1.0-alpha.1" dotenv = "0.13.0" env_logger = "0.5.10" futures = "0.1.22" diff --git a/actix_todo/src/api.rs b/actix_todo/src/api.rs index fcb67a0c..cb3e15ae 100644 --- a/actix_todo/src/api.rs +++ b/actix_todo/src/api.rs @@ -1,41 +1,34 @@ -use actix::prelude::Addr; -use actix_web::middleware::Response; -use actix_web::{ - error, fs::NamedFile, http, AsyncResponder, Form, FutureResponse, HttpRequest, - HttpResponse, Path, Responder, Result, -}; -use futures::{future, Future}; +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 db::{AllTasks, CreateTask, DbExecutor, DeleteTask, ToggleTask}; -use session::{self, FlashMessage}; +use crate::db; +use crate::session::{self, FlashMessage}; -pub struct AppState { - pub template: Tera, - pub db: Addr, -} - -pub fn index(req: HttpRequest) -> FutureResponse { - req.state() - .db - .send(AllTasks) +pub fn index( + pool: web::Data, + tmpl: web::Data, + session: Session, +) -> impl Future { + web::block(move || db::get_all_tasks(&pool)) .from_err() - .and_then(move |res| match res { + .then(move |res| match res { Ok(tasks) => { let mut context = Context::new(); - context.add("tasks", &tasks); + context.insert("tasks", &tasks); //Session is set during operations on other endpoints //that can redirect to index - if let Some(flash) = session::get_flash(&req)? { - context.add("msg", &(flash.kind, flash.message)); - session::clear_flash(&req); + if let Some(flash) = session::get_flash(&session)? { + context.insert("msg", &(flash.kind, flash.message)); + session::clear_flash(&session); } - let rendered = req.state() - .template - .render("index.html.tera", &context) - .map_err(|e| { + let rendered = + tmpl.render("index.html.tera", &context).map_err(|e| { error::ErrorInternalServerError(e.description().to_owned()) })?; @@ -43,7 +36,6 @@ pub fn index(req: HttpRequest) -> FutureResponse { } Err(e) => Err(e), }) - .responder() } #[derive(Deserialize)] @@ -52,34 +44,34 @@ pub struct CreateForm { } pub fn create( - (req, params): (HttpRequest, Form), -) -> FutureResponse { + params: web::Form, + pool: web::Data, + session: Session, +) -> impl Future { if params.description.is_empty() { - future::lazy(move || { + Either::A( session::set_flash( - &req, + &session, FlashMessage::error("Description cannot be empty"), - )?; - Ok(redirect_to("/")) - }).responder() + ) + .map(|_| redirect_to("/")) + .into_future(), + ) } else { - req.state() - .db - .send(CreateTask { - description: params.description.clone(), - }) - .from_err() - .and_then(move |res| match res { - Ok(_) => { - session::set_flash( - &req, - FlashMessage::success("Task successfully added"), - )?; - Ok(redirect_to("/")) - } - Err(e) => Err(e), - }) - .responder() + 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), + }), + ) } } @@ -94,49 +86,50 @@ pub struct UpdateForm { } pub fn update( - (req, params, form): (HttpRequest, Path, Form), -) -> FutureResponse { + db: web::Data, + params: web::Path, + form: web::Form, + session: Session, +) -> impl Future { match form._method.as_ref() { - "put" => toggle(req, params), - "delete" => delete(req, params), + "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); - future::err(error::ErrorBadRequest(msg)).responder() + Either::B(err(error::ErrorBadRequest(msg))) } } } fn toggle( - req: HttpRequest, - params: Path, -) -> FutureResponse { - req.state() - .db - .send(ToggleTask { id: params.id }) + pool: web::Data, + params: web::Path, +) -> impl Future { + web::block(move || db::toggle_task(params.id, &pool)) .from_err() - .and_then(move |res| match res { + .then(move |res| match res { Ok(_) => Ok(redirect_to("/")), Err(e) => Err(e), }) - .responder() } fn delete( - req: HttpRequest, - params: Path, -) -> FutureResponse { - req.state() - .db - .send(DeleteTask { id: params.id }) + pool: web::Data, + params: web::Path, + session: Session, +) -> impl Future { + web::block(move || db::delete_task(params.id, &pool)) .from_err() - .and_then(move |res| match res { + .then(move |res| match res { Ok(_) => { - session::set_flash(&req, FlashMessage::success("Task was deleted."))?; + session::set_flash( + &session, + FlashMessage::success("Task was deleted."), + )?; Ok(redirect_to("/")) } Err(e) => Err(e), }) - .responder() } fn redirect_to(location: &str) -> HttpResponse { @@ -145,32 +138,31 @@ fn redirect_to(location: &str) -> HttpResponse { .finish() } -pub fn bad_request( - req: &HttpRequest, - resp: HttpResponse, -) -> Result { +pub fn bad_request(res: dev::ServiceResponse) -> Result> { let new_resp = NamedFile::open("static/errors/400.html")? - .set_status_code(resp.status()) - .respond_to(req)?; - Ok(Response::Done(new_resp)) + .set_status_code(res.status()) + .respond_to(res.request())?; + Ok(ErrorHandlerResponse::Response( + res.into_response(new_resp.into_body()), + )) } -pub fn not_found( - req: &HttpRequest, - resp: HttpResponse, -) -> Result { +pub fn not_found(res: dev::ServiceResponse) -> Result> { let new_resp = NamedFile::open("static/errors/404.html")? - .set_status_code(resp.status()) - .respond_to(req)?; - Ok(Response::Done(new_resp)) + .set_status_code(res.status()) + .respond_to(res.request())?; + Ok(ErrorHandlerResponse::Response( + res.into_response(new_resp.into_body()), + )) } -pub fn internal_server_error( - req: &HttpRequest, - resp: HttpResponse, -) -> Result { +pub fn internal_server_error( + res: dev::ServiceResponse, +) -> Result> { let new_resp = NamedFile::open("static/errors/500.html")? - .set_status_code(resp.status()) - .respond_to(req)?; - Ok(Response::Done(new_resp)) + .set_status_code(res.status()) + .respond_to(res.request())?; + Ok(ErrorHandlerResponse::Response( + res.into_response(new_resp.into_body()), + )) } diff --git a/actix_todo/src/db.rs b/actix_todo/src/db.rs index 534ae648..579b0b5d 100644 --- a/actix_todo/src/db.rs +++ b/actix_todo/src/db.rs @@ -1,13 +1,11 @@ use std::ops::Deref; -use actix::prelude::{Actor, Handler, Message, SyncContext}; -use actix_web::{error, Error}; use diesel::pg::PgConnection; use diesel::r2d2::{ConnectionManager, Pool, PoolError, PooledConnection}; -use model::{NewTask, Task}; +use crate::model::{NewTask, Task}; -type PgPool = Pool>; +pub type PgPool = Pool>; type PgPooledConnection = PooledConnection>; pub fn init_pool(database_url: &str) -> Result { @@ -15,86 +13,29 @@ pub fn init_pool(database_url: &str) -> Result { Pool::builder().build(manager) } -pub struct DbExecutor(pub PgPool); - -impl DbExecutor { - pub fn get_conn(&self) -> Result { - self.0.get().map_err(|e| error::ErrorInternalServerError(e)) - } +fn get_conn(pool: &PgPool) -> Result { + pool.get().map_err(|_| "can get connection") } -impl Actor for DbExecutor { - type Context = SyncContext; +pub fn get_all_tasks(pool: &PgPool) -> Result, &'static str> { + Task::all(get_conn(pool)?.deref()).map_err(|_| "Error inserting task") } -pub struct AllTasks; - -impl Message for AllTasks { - type Result = Result, Error>; +pub fn create_task(todo: String, pool: &PgPool) -> Result<(), &'static str> { + let new_task = NewTask { description: todo }; + Task::insert(new_task, get_conn(pool)?.deref()) + .map(|_| ()) + .map_err(|_| "Error inserting task") } -impl Handler for DbExecutor { - type Result = Result, Error>; - - fn handle(&mut self, _: AllTasks, _: &mut Self::Context) -> Self::Result { - Task::all(self.get_conn()?.deref()) - .map_err(|_| error::ErrorInternalServerError("Error inserting task")) - } +pub fn toggle_task(id: i32, pool: &PgPool) -> Result<(), &'static str> { + Task::toggle_with_id(id, get_conn(pool)?.deref()) + .map(|_| ()) + .map_err(|_| "Error inserting task") } -pub struct CreateTask { - pub description: String, -} - -impl Message for CreateTask { - type Result = Result<(), Error>; -} - -impl Handler for DbExecutor { - type Result = Result<(), Error>; - - fn handle(&mut self, todo: CreateTask, _: &mut Self::Context) -> Self::Result { - let new_task = NewTask { - description: todo.description, - }; - Task::insert(new_task, self.get_conn()?.deref()) - .map(|_| ()) - .map_err(|_| error::ErrorInternalServerError("Error inserting task")) - } -} - -pub struct ToggleTask { - pub id: i32, -} - -impl Message for ToggleTask { - type Result = Result<(), Error>; -} - -impl Handler for DbExecutor { - type Result = Result<(), Error>; - - fn handle(&mut self, task: ToggleTask, _: &mut Self::Context) -> Self::Result { - Task::toggle_with_id(task.id, self.get_conn()?.deref()) - .map(|_| ()) - .map_err(|_| error::ErrorInternalServerError("Error inserting task")) - } -} - -pub struct DeleteTask { - pub id: i32, -} - -impl Message for DeleteTask { - type Result = Result<(), Error>; -} - -impl Handler for DbExecutor { - type Result = Result<(), Error>; - - fn handle(&mut self, task: DeleteTask, _: &mut Self::Context) -> Self::Result { - Task::delete_with_id(task.id, self.get_conn()?.deref()) - .map(|_| ()) - .map_err(|_| error::ErrorInternalServerError("Error inserting task")) - } +pub fn delete_task(id: i32, pool: &PgPool) -> Result<(), &'static str> { + Task::delete_with_id(id, get_conn(pool)?.deref()) + .map(|_| ()) + .map_err(|_| "Error inserting task") } diff --git a/actix_todo/src/main.rs b/actix_todo/src/main.rs index f2de26af..aca105ec 100644 --- a/actix_todo/src/main.rs +++ b/actix_todo/src/main.rs @@ -1,8 +1,3 @@ -extern crate actix; -extern crate actix_web; -extern crate dotenv; -extern crate env_logger; -extern crate futures; #[macro_use] extern crate diesel; #[macro_use] @@ -12,12 +7,13 @@ extern crate serde_derive; #[macro_use] extern crate tera; -use actix::prelude::SyncArbiter; -use actix_web::middleware::session::{CookieSessionBackend, SessionStorage}; -use actix_web::middleware::{ErrorHandlers, Logger}; -use actix_web::{dev::Resource, fs, http, server, App}; +use std::{env, io}; + +use actix_files as fs; +use actix_session::CookieSession; +use actix_web::middleware::{errhandlers::ErrorHandlers, Logger}; +use actix_web::{http, web, App, HttpServer}; use dotenv::dotenv; -use std::env; use tera::Tera; mod api; @@ -27,29 +23,22 @@ mod schema; mod session; static SESSION_SIGNING_KEY: &[u8] = &[0; 32]; -const NUM_DB_THREADS: usize = 3; -fn main() { +fn main() -> io::Result<()> { dotenv().ok(); - std::env::set_var("RUST_LOG", "actix_todo=debug,actix_web=info"); + env::set_var("RUST_LOG", "actix_todo=debug,actix_web=info"); env_logger::init(); - // Start the Actix system - let system = actix::System::new("todo-app"); - let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); let pool = db::init_pool(&database_url).expect("Failed to create pool"); - let addr = SyncArbiter::start(NUM_DB_THREADS, move || db::DbExecutor(pool.clone())); let app = move || { debug!("Constructing the App"); let templates: Tera = compile_templates!("templates/**/*"); - let session_store = SessionStorage::new( - CookieSessionBackend::signed(SESSION_SIGNING_KEY).secure(false), - ); + let session_store = CookieSession::signed(SESSION_SIGNING_KEY).secure(false); let error_handlers = ErrorHandlers::new() .handler( @@ -59,29 +48,20 @@ fn main() { .handler(http::StatusCode::BAD_REQUEST, api::bad_request) .handler(http::StatusCode::NOT_FOUND, api::not_found); - let static_files = fs::StaticFiles::new("static/") - .expect("failed constructing static files handler"); - - let state = api::AppState { - template: templates, - db: addr.clone(), - }; - - App::with_state(state) - .middleware(Logger::default()) - .middleware(session_store) - .middleware(error_handlers) - .route("/", http::Method::GET, api::index) - .route("/todo", http::Method::POST, api::create) - .resource("/todo/{id}", |r: &mut Resource<_>| { - r.post().with(api::update) - }) - .handler("/static", static_files) + App::new() + .data(templates) + .data(pool.clone()) + .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(fs::Files::new("/static", "static/")) }; debug!("Starting server"); - server::new(app).bind("localhost:8088").unwrap().start(); - - // Run actix system, this method actually starts all async processes - let _ = system.run(); + HttpServer::new(app).bind("localhost:8088")?.run() } diff --git a/actix_todo/src/model.rs b/actix_todo/src/model.rs index 3f1afb13..4b80f4a8 100644 --- a/actix_todo/src/model.rs +++ b/actix_todo/src/model.rs @@ -2,8 +2,9 @@ use diesel; use diesel::pg::PgConnection; use diesel::prelude::*; -use schema::{ - tasks, tasks::dsl::{completed as task_completed, tasks as all_tasks}, +use crate::schema::{ + tasks, + tasks::dsl::{completed as task_completed, tasks as all_tasks}, }; #[derive(Debug, Insertable)] diff --git a/actix_todo/src/session.rs b/actix_todo/src/session.rs index c4c6aadb..d1cfc7a4 100644 --- a/actix_todo/src/session.rs +++ b/actix_todo/src/session.rs @@ -1,19 +1,18 @@ +use actix_session::Session; use actix_web::error::Result; -use actix_web::middleware::session::RequestSession; -use actix_web::HttpRequest; const FLASH_KEY: &str = "flash"; -pub fn set_flash(request: &HttpRequest, flash: FlashMessage) -> Result<()> { - request.session().set(FLASH_KEY, flash) +pub fn set_flash(session: &Session, flash: FlashMessage) -> Result<()> { + session.set(FLASH_KEY, flash) } -pub fn get_flash(req: &HttpRequest) -> Result> { - req.session().get::(FLASH_KEY) +pub fn get_flash(session: &Session) -> Result> { + session.get::(FLASH_KEY) } -pub fn clear_flash(req: &HttpRequest) { - req.session().remove(FLASH_KEY); +pub fn clear_flash(session: &Session) { + session.remove(FLASH_KEY); } #[derive(Deserialize, Serialize)] diff --git a/async_db/Cargo.toml b/async_db/Cargo.toml index 55105f57..2d322de3 100644 --- a/async_db/Cargo.toml +++ b/async_db/Cargo.toml @@ -2,18 +2,21 @@ name = "async_db" version = "0.1.0" authors = ["Darin Gordon "] +edition = "2018" +workspace = ".." [dependencies] -actix = "0.7" -actix-web = "0.7" +actix-rt = "0.2" +actix-web = "1.0.0-alpha.1" dotenv = "0.10" env_logger = "0.5" failure = "0.1.1" futures = "0.1" -num_cpus = "1.8.0" +num_cpus = "1.10.0" r2d2 = "0.8.2" -r2d2_sqlite = "0.5.0" +r2d2_sqlite = "0.8.0" +rusqlite = "0.16" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" diff --git a/async_db/src/db.rs b/async_db/src/db.rs index a7903d71..4ca80daf 100644 --- a/async_db/src/db.rs +++ b/async_db/src/db.rs @@ -1,17 +1,15 @@ -use actix::prelude::*; +use actix_web::{web, Error as AWError}; use failure::Error; +use futures::Future; use r2d2; use r2d2_sqlite; +use rusqlite::NO_PARAMS; +use serde_derive::{Deserialize, Serialize}; use std::{thread::sleep, time::Duration}; pub type Pool = r2d2::Pool; pub type Connection = r2d2::PooledConnection; -pub struct DbExecutor(pub Pool); -impl Actor for DbExecutor { - type Context = SyncContext; -} - #[derive(Debug, Serialize, Deserialize)] pub enum WeatherAgg { AnnualAgg { year: i32, total: f64 }, @@ -25,29 +23,24 @@ pub enum Queries { GetTopTenColdestMonths, } -//pub struct GetTopTenHottestYears; -impl Message for Queries { - type Result = Result, Error>; -} -impl Handler for DbExecutor { - type Result = Result, Error>; - - fn handle(&mut self, msg: Queries, _: &mut Self::Context) -> Self::Result { - let conn: Connection = self.0.get()?; - - match msg { - Queries::GetTopTenHottestYears => get_hottest_years(conn), - Queries::GetTopTenColdestYears => get_coldest_years(conn), - Queries::GetTopTenHottestMonths => get_hottest_months(conn), - Queries::GetTopTenColdestMonths => get_coldest_months(conn), - } - } +pub fn execute( + pool: &Pool, + query: Queries, +) -> impl Future, Error = AWError> { + let pool = pool.clone(); + web::block(move || match query { + Queries::GetTopTenHottestYears => get_hottest_years(pool.get()?), + Queries::GetTopTenColdestYears => get_coldest_years(pool.get()?), + Queries::GetTopTenHottestMonths => get_hottest_months(pool.get()?), + Queries::GetTopTenColdestMonths => get_coldest_months(pool.get()?), + }) + .from_err() } fn get_hottest_years(conn: Connection) -> Result, Error> { let stmt = " SELECT cast(strftime('%Y', date) as int) as theyear, - sum(tmax) as total + sum(tmax) as total FROM nyc_weather WHERE tmax <> 'TMAX' GROUP BY theyear @@ -55,7 +48,7 @@ fn get_hottest_years(conn: Connection) -> Result, Error> { let mut prep_stmt = conn.prepare(stmt)?; let annuals = prep_stmt - .query_map(&[], |row| WeatherAgg::AnnualAgg { + .query_map(NO_PARAMS, |row| WeatherAgg::AnnualAgg { year: row.get(0), total: row.get(1), }) @@ -73,7 +66,7 @@ fn get_hottest_years(conn: Connection) -> Result, Error> { fn get_coldest_years(conn: Connection) -> Result, Error> { let stmt = " SELECT cast(strftime('%Y', date) as int) as theyear, - sum(tmax) as total + sum(tmax) as total FROM nyc_weather WHERE tmax <> 'TMAX' GROUP BY theyear @@ -81,7 +74,7 @@ fn get_coldest_years(conn: Connection) -> Result, Error> { let mut prep_stmt = conn.prepare(stmt)?; let annuals = prep_stmt - .query_map(&[], |row| WeatherAgg::AnnualAgg { + .query_map(NO_PARAMS, |row| WeatherAgg::AnnualAgg { year: row.get(0), total: row.get(1), }) @@ -98,8 +91,8 @@ fn get_coldest_years(conn: Connection) -> Result, Error> { fn get_hottest_months(conn: Connection) -> Result, Error> { let stmt = "SELECT cast(strftime('%Y', date) as int) as theyear, - cast(strftime('%m', date) as int) as themonth, - sum(tmax) as total + cast(strftime('%m', date) as int) as themonth, + sum(tmax) as total FROM nyc_weather WHERE tmax <> 'TMAX' GROUP BY theyear, themonth @@ -107,7 +100,7 @@ fn get_hottest_months(conn: Connection) -> Result, Error> { let mut prep_stmt = conn.prepare(stmt)?; let annuals = prep_stmt - .query_map(&[], |row| WeatherAgg::MonthAgg { + .query_map(NO_PARAMS, |row| WeatherAgg::MonthAgg { year: row.get(0), month: row.get(1), total: row.get(2), @@ -124,8 +117,8 @@ fn get_hottest_months(conn: Connection) -> Result, Error> { fn get_coldest_months(conn: Connection) -> Result, Error> { let stmt = "SELECT cast(strftime('%Y', date) as int) as theyear, - cast(strftime('%m', date) as int) as themonth, - sum(tmax) as total + cast(strftime('%m', date) as int) as themonth, + sum(tmax) as total FROM nyc_weather WHERE tmax <> 'TMAX' GROUP BY theyear, themonth @@ -133,7 +126,7 @@ fn get_coldest_months(conn: Connection) -> Result, Error> { let mut prep_stmt = conn.prepare(stmt)?; let annuals = prep_stmt - .query_map(&[], |row| WeatherAgg::MonthAgg { + .query_map(NO_PARAMS, |row| WeatherAgg::MonthAgg { year: row.get(0), month: row.get(1), total: row.get(2), diff --git a/async_db/src/main.rs b/async_db/src/main.rs index 42dcb1bd..b2293fed 100644 --- a/async_db/src/main.rs +++ b/async_db/src/main.rs @@ -8,122 +8,90 @@ This project illustrates two examples: 2. An asynchronous handler that executes 4 queries in *parallel*, collecting the results and returning them as a single serialized json object -*/ + */ +use std::io; -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate failure; -extern crate futures; -extern crate num_cpus; -extern crate r2d2; -extern crate r2d2_sqlite; -extern crate serde; -#[macro_use] -extern crate serde_derive; -extern crate serde_json; - -use actix::prelude::*; -use actix_web::{ - http, middleware, server, App, AsyncResponder, Error as AWError, FutureResponse, - HttpResponse, State, -}; +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; mod db; -use db::{DbExecutor, Pool, Queries, WeatherAgg}; - -/// State with DbExecutor address -struct AppState { - db: Addr, -} +use db::{Pool, Queries, WeatherAgg}; /// Version 1: Calls 4 queries in sequential order, as an asynchronous handler -fn asyncio_weather(state: State) -> FutureResponse { +fn asyncio_weather( + db: web::Data, +) -> impl Future { let mut result: Vec> = vec![]; - state - .db - .send(Queries::GetTopTenHottestYears) + db::execute(&db, Queries::GetTopTenHottestYears) .from_err() .and_then(move |res| { - result.push(res.unwrap()); - state - .db - .send(Queries::GetTopTenColdestYears) + result.push(res); + db::execute(&db, Queries::GetTopTenColdestYears) .from_err() .and_then(move |res| { - result.push(res.unwrap()); - state - .db - .send(Queries::GetTopTenHottestMonths) + result.push(res); + db::execute(&db, Queries::GetTopTenHottestMonths) .from_err() .and_then(move |res| { - result.push(res.unwrap()); - state - .db - .send(Queries::GetTopTenColdestMonths) + result.push(res); + db::execute(&db, Queries::GetTopTenColdestMonths) .from_err() .and_then(move |res| { - result.push(res.unwrap()); + result.push(res); fut_ok(result) }) }) }) }) .and_then(|res| Ok(HttpResponse::Ok().json(res))) - .responder() } /// Version 2: Calls 4 queries in parallel, as an asynchronous handler /// Returning Error types turn into None values in the response -fn parallel_weather(state: State) -> FutureResponse { +fn parallel_weather( + db: web::Data, +) -> impl Future { let fut_result = vec![ - Box::new(state.db.send(Queries::GetTopTenHottestYears)), - Box::new(state.db.send(Queries::GetTopTenColdestYears)), - Box::new(state.db.send(Queries::GetTopTenHottestMonths)), - Box::new(state.db.send(Queries::GetTopTenColdestMonths)), + 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)), ]; join_all(fut_result) .map_err(AWError::from) - .and_then(|result| { - let res: Vec>> = - result.into_iter().map(|x| x.ok()).collect(); - - Ok(HttpResponse::Ok().json(res)) - }) - .responder() + .map(|result| HttpResponse::Ok().json(result)) } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("parallel_db_example"); + 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"); let pool = Pool::new(manager).unwrap(); - let addr = SyncArbiter::start(num_cpus::get(), move || DbExecutor(pool.clone())); - // Start http server - server::new(move || { - App::with_state(AppState{db: addr.clone()}) - // enable logger - .middleware(middleware::Logger::default()) - .resource("/asyncio_weather", |r| - r.method(http::Method::GET) - .with(asyncio_weather)) - .resource("/parallel_weather", |r| - r.method(http::Method::GET) - .with(parallel_weather)) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); + HttpServer::new(move || { + App::new() + .data(pool.clone()) + .wrap(middleware::Logger::default()) + .service( + web::resource("/asyncio_weather") + .route(web::get().to_async(asyncio_weather)), + ) + .service( + web::resource("/parallel_weather") + .route(web::get().to_async(parallel_weather)), + ) + }) + .bind("127.0.0.1:8080")? + .start(); println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + sys.run() } diff --git a/async_ex1/Cargo.toml b/async_ex1/Cargo.toml index 1c30e0f9..1be43ae9 100644 --- a/async_ex1/Cargo.toml +++ b/async_ex1/Cargo.toml @@ -2,10 +2,12 @@ name = "awc_examples" version = "0.1.0" authors = ["dowwie "] +edition = "2018" +workspace = ".." [dependencies] -actix = "0.7" -actix-web = { version="0.7.3", features=["rust-tls"] } +actix-rt = "0.2" +actix-web = { version="1.0.0-alpha.1", features=["ssl"] } futures = "0.1" serde = "1.0.43" diff --git a/async_ex1/src/main.rs b/async_ex1/src/main.rs index 917443b1..a9eb054b 100644 --- a/async_ex1/src/main.rs +++ b/async_ex1/src/main.rs @@ -15,25 +15,18 @@ // There are 2 versions in this example, one that uses Boxed Futures and the // other that uses Impl Future, available since rustc v1.26. -extern crate actix; -extern crate actix_web; -extern crate serde; -#[macro_use] -extern crate serde_derive; -extern crate serde_json; #[macro_use] extern crate validator_derive; -extern crate env_logger; -extern crate futures; -extern crate validator; +#[macro_use] +extern crate serde_derive; -use actix_web::{ - client, http::Method, server, App, AsyncResponder, Error, HttpMessage, HttpResponse, - Json, error::ErrorBadRequest, -}; -use futures::{future::ok as fut_ok, future::result as fut_result, Future}; use std::collections::HashMap; -use std::time::Duration; +use std::io; + +use actix_web::client::Client; +use actix_web::web::BytesMut; +use actix_web::{web, App, Error, HttpResponse, HttpServer}; +use futures::{Future, Stream}; use validator::Validate; #[derive(Debug, Validate, Deserialize, Serialize)] @@ -61,43 +54,47 @@ struct HttpBinResponse { // ----------------------------------------------------------------------- /// post json to httpbin, get it back in the response body, return deserialized -fn step_x_v1(data: SomeData) -> Box> { +fn step_x_v1( + data: SomeData, + client: &Client, +) -> Box> { Box::new( - fut_result(data.validate()) // <- call .validate() on data to validate the parameters - .map_err(ErrorBadRequest) // - convert ValidationErrors to an Error - .and_then(|_| { - client::ClientRequest::post("https://httpbin.org/post") - .json(data).unwrap() - .send() - .conn_timeout(Duration::from_secs(10)) - .map_err(Error::from) // <- convert SendRequestError to an Error - .and_then( - |resp| resp.body() // <- this is MessageBody type, resolves to complete body - .from_err() // <- convert PayloadError to an Error - .and_then(|body| { - let resp: HttpBinResponse = serde_json::from_slice(&body).unwrap(); - fut_ok(resp.json) - }) - ) - }) + client + .post("https://httpbin.org/post") + .send_json(data) + .map_err(Error::from) // <- convert SendRequestError to an Error + .and_then(|resp| { + resp // <- this is MessageBody type, resolves to complete body + .from_err() // <- convert PayloadError to an Error + .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 + }) + }), ) } fn create_something_v1( - some_data: Json, + some_data: web::Json, + client: web::Data, ) -> Box> { - step_x_v1(some_data.into_inner()) - .and_then(|some_data_2| { - step_x_v1(some_data_2).and_then(|some_data_3| { - step_x_v1(some_data_3).and_then(|d| { + Box::new( + step_x_v1(some_data.into_inner(), &client).and_then(move |some_data_2| { + step_x_v1(some_data_2, &client).and_then(move |some_data_3| { + step_x_v1(some_data_3, &client).and_then(|d| { Ok(HttpResponse::Ok() .content_type("application/json") .body(serde_json::to_string(&d).unwrap()) .into()) }) }) - }) - .responder() + }), + ) } // --------------------------------------------------------------- @@ -105,32 +102,34 @@ fn create_something_v1( // --------------------------------------------------------------- /// post json to httpbin, get it back in the response body, return deserialized -fn step_x_v2(data: SomeData) -> impl Future { - fut_result(data.validate()) // <- call .validate() on data to validate the parameters - .map_err(ErrorBadRequest) // - convert ValidationErrors to an Error - .and_then(|_| { - client::ClientRequest::post("https://httpbin.org/post") - .json(data).unwrap() - .send() - .conn_timeout(Duration::from_secs(10)) - .map_err(Error::from) // <- convert SendRequestError to an Error - .and_then( - |resp| resp.body() // <- this is MessageBody type, resolves to complete body - .from_err() // <- convert PayloadError to an Error - .and_then(|body| { - let resp: HttpBinResponse = serde_json::from_slice(&body).unwrap(); - fut_ok(resp.json) - }) - ) +fn step_x_v2( + data: SomeData, + client: &Client, +) -> impl Future { + 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 + }) }) } fn create_something_v2( - some_data: Json, + some_data: web::Json, + client: web::Data, ) -> impl Future { - step_x_v2(some_data.into_inner()).and_then(|some_data_2| { - step_x_v2(some_data_2).and_then(|some_data_3| { - step_x_v2(some_data_3).and_then(|d| { + step_x_v2(some_data.into_inner(), &client).and_then(move |some_data_2| { + step_x_v2(some_data_2, &client).and_then(move |some_data_3| { + step_x_v2(some_data_3, &client).and_then(|d| { Ok(HttpResponse::Ok() .content_type("application/json") .body(serde_json::to_string(&d).unwrap()) @@ -140,23 +139,22 @@ fn create_something_v2( }) } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("asyncio_example"); - server::new(move || { + HttpServer::new(|| { App::new() - .resource("/something_v1", |r| { - r.method(Method::POST).with(create_something_v1) - }) - .resource("/something_v2", |r| { - r.method(Method::POST).with_async(create_something_v2) - }) - }).bind("127.0.0.1:8088") - .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8088"); - let _ = sys.run(); + .data(Client::default()) + .service( + web::resource("/something_v1") + .route(web::post().to(create_something_v1)), + ) + .service( + web::resource("/something_v2") + .route(web::post().to_async(create_something_v2)), + ) + }) + .bind("127.0.0.1:8088")? + .run() } diff --git a/basics/Cargo.toml b/basics/Cargo.toml index 2db8e75a..64c08aa1 100644 --- a/basics/Cargo.toml +++ b/basics/Cargo.toml @@ -1,13 +1,16 @@ [package] name = "basics" -version = "0.1.0" +version = "1.0.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] -actix = "0.7" -actix-web = "0.7" +actix-rt = "0.2" +actix-web = "1.0.0-alpha.1" +actix-files = "0.1.0-alpha.1" +actix-session = "0.1.0-alpha.1" -futures = "0.1" +futures = "0.1.25" env_logger = "0.5" bytes = "0.4" diff --git a/basics/src/main.rs b/basics/src/main.rs index b4fbe9e9..7ab8a725 100644 --- a/basics/src/main.rs +++ b/basics/src/main.rs @@ -1,43 +1,39 @@ -#![allow(unused_variables)] -#![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] - -extern crate actix; +#[macro_use] extern crate actix_web; -extern crate bytes; -extern crate env_logger; -extern crate futures; -use bytes::Bytes; -use futures::sync::mpsc; -use futures::Stream; - -use actix_web::http::{header, Method, StatusCode}; -use actix_web::middleware::session::{self, RequestSession}; -use actix_web::{ - error, fs, middleware, pred, server, App, Error, HttpRequest, HttpResponse, Path, - Result, -}; -use futures::future::{result, FutureResult}; use std::{env, io}; +use actix_files as fs; +use actix_session::{CookieSession, Session}; +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 -fn favicon(req: &HttpRequest) -> Result { +#[get("/favicon")] +fn favicon() -> Result { Ok(fs::NamedFile::open("static/favicon.ico")?) } /// simple index handler -fn welcome(req: &HttpRequest) -> Result { +#[get("/welcome")] +fn welcome(session: Session, req: HttpRequest) -> Result { println!("{:?}", req); // session let mut counter = 1; - if let Some(count) = req.session().get::("counter")? { + if let Some(count) = session.get::("counter")? { println!("SESSION value: {}", count); counter = count + 1; } // set counter to session - req.session().set("counter", counter)?; + session.set("counter", counter)?; // response Ok(HttpResponse::build(StatusCode::OK) @@ -46,97 +42,102 @@ fn welcome(req: &HttpRequest) -> Result { } /// 404 handler -fn p404(req: &HttpRequest) -> Result { +fn p404() -> Result { Ok(fs::NamedFile::open("static/404.html")?.set_status_code(StatusCode::NOT_FOUND)) } /// async handler -fn index_async(req: &HttpRequest) -> FutureResult { +fn index_async(req: HttpRequest) -> impl Future { println!("{:?}", req); - result(Ok(HttpResponse::Ok().content_type("text/html").body( - format!("Hello {}!", req.match_info().get("name").unwrap()), - ))) + ok(HttpResponse::Ok() + .content_type("text/html") + .body(format!("Hello {}!", req.match_info().get("name").unwrap()))) } /// async body -fn index_async_body(path: Path) -> HttpResponse { +fn index_async_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())); HttpResponse::Ok() - .streaming(rx_body.map_err(|e| error::ErrorBadRequest("bad request"))) + .streaming(rx_body.map_err(|_| error::ErrorBadRequest("bad request"))) } /// handler with path parameters like `/user/{name}/` -fn with_param(req: &HttpRequest) -> HttpResponse { +fn with_param(req: HttpRequest, path: web::Path<(String,)>) -> HttpResponse { println!("{:?}", req); HttpResponse::Ok() .content_type("text/plain") - .body(format!("Hello {}!", req.match_info().get("name").unwrap())) + .body(format!("Hello {}!", path.0)) } -fn main() { +fn main() -> io::Result<()> { env::set_var("RUST_LOG", "actix_web=debug"); - env::set_var("RUST_BACKTRACE", "1"); env_logger::init(); - let sys = actix::System::new("basic-example"); + let sys = actix_rt::System::new("basic-example"); - let addr = server::new( - || App::new() + HttpServer::new(|| { + App::new() // enable logger - .middleware(middleware::Logger::default()) + .wrap(middleware::Logger::default()) // cookie session middleware - .middleware(session::SessionStorage::new( - session::CookieSessionBackend::signed(&[0; 32]).secure(false) - )) + .wrap(CookieSession::signed(&[0; 32]).secure(false)) // register favicon - .resource("/favicon", |r| r.f(favicon)) + .service(favicon) // register simple route, handle all methods - .resource("/welcome", |r| r.f(welcome)) + .service(welcome) // with path parameters - .resource("/user/{name}", |r| r.method(Method::GET).f(with_param)) + .service(web::resource("/user/{name}").route(web::get().to(with_param))) // async handler - .resource("/async/{name}", |r| r.method(Method::GET).a(index_async)) + .service( + web::resource("/async/{name}").route(web::get().to_async(index_async)), + ) // async handler - .resource("/async-body/{name}", |r| r.method(Method::GET).with(index_async_body)) - .resource("/test", |r| r.f(|req| { - match *req.method() { + .service( + web::resource("/async-body/{name}") + .route(web::get().to(index_async_body)), + ) + .service( + web::resource("/test").to(|req: HttpRequest| match *req.method() { Method::GET => HttpResponse::Ok(), Method::POST => HttpResponse::MethodNotAllowed(), _ => HttpResponse::NotFound(), - } - })) - .resource("/error", |r| r.f(|req| { + }), + ) + .service(web::resource("/error").to(|| { error::InternalError::new( - io::Error::new(io::ErrorKind::Other, "test"), StatusCode::INTERNAL_SERVER_ERROR) + io::Error::new(io::ErrorKind::Other, "test"), + StatusCode::INTERNAL_SERVER_ERROR, + ) })) // static files - .handler("/static", fs::StaticFiles::new("static").unwrap()) + .service(fs::Files::new("/static", "static").show_files_listing()) // redirect - .resource("/", |r| r.method(Method::GET).f(|req| { + .service(web::resource("/").route(web::get().to(|req: HttpRequest| { println!("{:?}", req); HttpResponse::Found() .header(header::LOCATION, "static/welcome.html") .finish() - })) + }))) // default .default_resource(|r| { // 404 for GET request - r.method(Method::GET).f(p404); - - // all requests that are not `GET` - r.route().filter(pred::Not(pred::Get())).f( - |req| HttpResponse::MethodNotAllowed()); - })) - - .bind("127.0.0.1:8080").expect("Can not bind to 127.0.0.1:8080") - .shutdown_timeout(0) // <- Set shutdown timeout to 0 seconds (default 60s) - .start(); + r.route(web::get().to(p404)) + // all requests that are not `GET` + .route( + web::route() + .guard(guard::Not(guard::Get())) + .to(|| HttpResponse::MethodNotAllowed()), + ) + }) + }) + .bind("127.0.0.1:8080")? + .start(); println!("Starting http server: 127.0.0.1:8080"); - let _ = sys.run(); + sys.run() } diff --git a/cookie-auth-full/Cargo.toml b/cookie-auth-full/Cargo.toml deleted file mode 100644 index 87e6df95..00000000 --- a/cookie-auth-full/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "cookie-auth-full" -version = "0.1.0" -authors = ["Nikolay Kim "] -workspace = "../" - -[dependencies] -actix = "0.7" -actix-web = "0.7" - -cookie = { version="0.11", features=["percent-encode", "secure"] } -futures = "0.1" -time = "0.1" -env_logger = "0.5" diff --git a/cookie-auth-full/src/auth.rs b/cookie-auth-full/src/auth.rs deleted file mode 100644 index 56ec54e4..00000000 --- a/cookie-auth-full/src/auth.rs +++ /dev/null @@ -1,274 +0,0 @@ -#![allow(dead_code)] -use std::rc::Rc; - -use cookie::{Cookie, CookieJar, Key}; -use futures::future::{err as FutErr, ok as FutOk, FutureResult}; -use futures::Future; -use time::Duration; - -use actix_web::http::header::{self, HeaderValue}; -use actix_web::middleware::{Middleware, Response, Started}; -use actix_web::{Error, HttpRequest, HttpResponse, Result}; - -/// Trait provides identity service for the request. -pub trait RequestIdentity { - /// Return the claimed identity of the user associated request or - /// ``None`` if no identity can be found associated with the request. - fn identity(&self) -> Option; - - /// Remember identity. - fn remember(&self, identity: String); - - /// This method is used to 'forget' the current identity on subsequent - /// requests. - fn forget(&self); -} - -impl RequestIdentity for HttpRequest { - fn identity(&self) -> Option { - if let Some(id) = self.extensions().get::() { - return id.0.identity().map(|s| s.to_owned()); - } - None - } - - fn remember(&self, identity: String) { - if let Some(id) = self.extensions_mut().get_mut::() { - return id.0.as_mut().remember(identity); - } - } - - fn forget(&self) { - if let Some(id) = self.extensions_mut().get_mut::() { - return id.0.forget(); - } - } -} - -/// An identity -pub trait Identity: 'static { - fn identity(&self) -> Option<&str>; - - fn remember(&mut self, key: String); - - fn forget(&mut self); - - /// Write session to storage backend. - fn write(&mut self, resp: HttpResponse) -> Result; -} - -/// Identity policy definition. -pub trait IdentityPolicy: Sized + 'static { - type Identity: Identity; - type Future: Future; - - /// Parse the session from request and load data from a service identity. - fn from_request(&self, request: &mut HttpRequest) -> Self::Future; -} - -/// Middleware that implements identity service -pub struct IdentityService { - backend: T, -} - -impl IdentityService { - /// Create new identity service with specified backend. - pub fn new(backend: T) -> Self { - IdentityService { backend } - } -} - -struct IdentityBox(Box); - -#[doc(hidden)] -unsafe impl Send for IdentityBox {} -#[doc(hidden)] -unsafe impl Sync for IdentityBox {} - -impl> Middleware for IdentityService { - fn start(&self, req: &HttpRequest) -> Result { - let mut req = req.clone(); - - let fut = self - .backend - .from_request(&mut req) - .then(move |res| match res { - Ok(id) => { - req.extensions_mut().insert(IdentityBox(Box::new(id))); - FutOk(None) - } - Err(err) => FutErr(err), - }); - Ok(Started::Future(Box::new(fut))) - } - - fn response(&self, req: &HttpRequest, resp: HttpResponse) -> Result { - if let Some(mut id) = req.extensions_mut().remove::() { - id.0.write(resp) - } else { - Ok(Response::Done(resp)) - } - } -} - -/// Identity that uses private cookies as identity storage -pub struct CookieIdentity { - changed: bool, - identity: Option, - inner: Rc, -} - -impl Identity for CookieIdentity { - fn identity(&self) -> Option<&str> { - self.identity.as_ref().map(|s| s.as_ref()) - } - - fn remember(&mut self, value: String) { - self.changed = true; - self.identity = Some(value); - } - - fn forget(&mut self) { - self.changed = true; - self.identity = None; - } - - fn write(&mut self, mut resp: HttpResponse) -> Result { - if self.changed { - let _ = self.inner.set_cookie(&mut resp, self.identity.take()); - } - Ok(Response::Done(resp)) - } -} - -struct CookieIdentityInner { - key: Key, - name: String, - path: String, - domain: Option, - secure: bool, - max_age: Option, -} - -impl CookieIdentityInner { - fn new(key: &[u8]) -> CookieIdentityInner { - CookieIdentityInner { - key: Key::from_master(key), - name: "actix-identity".to_owned(), - path: "/".to_owned(), - domain: None, - secure: true, - max_age: None, - } - } - - fn set_cookie(&self, resp: &mut HttpResponse, id: Option) -> Result<()> { - let some = id.is_some(); - { - let id = id.unwrap_or_else(|| String::new()); - let mut cookie = Cookie::new(self.name.clone(), id); - cookie.set_path(self.path.clone()); - cookie.set_secure(self.secure); - cookie.set_http_only(true); - - if let Some(ref domain) = self.domain { - cookie.set_domain(domain.clone()); - } - - if let Some(max_age) = self.max_age { - cookie.set_max_age(max_age); - } - - let mut jar = CookieJar::new(); - if some { - jar.private(&self.key).add(cookie); - } else { - jar.add_original(cookie.clone()); - jar.private(&self.key).remove(cookie); - } - - for cookie in jar.delta() { - let val = HeaderValue::from_str(&cookie.to_string())?; - resp.headers_mut().append(header::SET_COOKIE, val); - } - } - - Ok(()) - } - - fn load(&self, req: &mut HttpRequest) -> Option { - if let Ok(cookies) = req.cookies() { - for cookie in cookies.iter() { - if cookie.name() == self.name { - let mut jar = CookieJar::new(); - jar.add_original(cookie.clone()); - - let cookie_opt = jar.private(&self.key).get(&self.name); - if let Some(cookie) = cookie_opt { - return Some(cookie.value().into()); - } - } - } - } - None - } -} - -/// Use cookies for request identity. -pub struct CookieIdentityPolicy(Rc); - -impl CookieIdentityPolicy { - /// Construct new `CookieIdentityPolicy` instance. - /// - /// Panics if key length is less than 32 bytes. - pub fn new(key: &[u8]) -> CookieIdentityPolicy { - CookieIdentityPolicy(Rc::new(CookieIdentityInner::new(key))) - } - - /// Sets the `path` field in the session cookie being built. - pub fn path>(mut self, value: S) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().path = value.into(); - self - } - - /// Sets the `name` field in the session cookie being built. - pub fn name>(mut self, value: S) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().name = value.into(); - self - } - - /// Sets the `domain` field in the session cookie being built. - pub fn domain>(mut self, value: S) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().domain = Some(value.into()); - self - } - - /// Sets the `secure` field in the session cookie being built. - /// - /// If the `secure` field is set, a cookie will only be transmitted when the - /// connection is secure - i.e. `https` - pub fn secure(mut self, value: bool) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().secure = value; - self - } - - /// Sets the `max-age` field in the session cookie being built. - pub fn max_age(mut self, value: Duration) -> CookieIdentityPolicy { - Rc::get_mut(&mut self.0).unwrap().max_age = Some(value); - self - } -} - -impl IdentityPolicy for CookieIdentityPolicy { - type Identity = CookieIdentity; - type Future = FutureResult; - - fn from_request(&self, req: &mut HttpRequest) -> Self::Future { - let identity = self.0.load(req); - FutOk(CookieIdentity { - identity, - changed: false, - inner: Rc::clone(&self.0), - }) - } -} diff --git a/cookie-auth-full/src/main.rs b/cookie-auth-full/src/main.rs deleted file mode 100644 index f7a0c09c..00000000 --- a/cookie-auth-full/src/main.rs +++ /dev/null @@ -1,49 +0,0 @@ -extern crate actix; -extern crate actix_web; -extern crate cookie; -extern crate env_logger; -extern crate futures; -extern crate time; - -use actix_web::{middleware, server, App, HttpRequest, HttpResponse}; - -mod auth; -use auth::{CookieIdentityPolicy, IdentityService, RequestIdentity}; - -fn index(req: &HttpRequest) -> String { - format!("Hello {}", req.identity().unwrap_or("Anonymous".to_owned())) -} - -fn login(req: &HttpRequest) -> HttpResponse { - req.remember("user1".to_owned()); - HttpResponse::Found().header("location", "/").finish() -} - -fn logout(req: &HttpRequest) -> HttpResponse { - req.forget(); - HttpResponse::Found().header("location", "/").finish() -} - -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); - env_logger::init(); - let sys = actix::System::new("cookie-auth"); - - server::new(|| { - App::new() - .middleware(middleware::Logger::default()) - .middleware(IdentityService::new( - CookieIdentityPolicy::new(&[0; 32]) - .name("auth-example") - .secure(false), - )) - .resource("/login", |r| r.f(login)) - .resource("/logout", |r| r.f(logout)) - .resource("/", |r| r.f(index)) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); -} diff --git a/cookie-auth/Cargo.toml b/cookie-auth/Cargo.toml index ceda654d..d728e3ed 100644 --- a/cookie-auth/Cargo.toml +++ b/cookie-auth/Cargo.toml @@ -2,9 +2,9 @@ name = "cookie-auth" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +edition = "2018" +workspace = ".." [dependencies] -actix = "0.7" -actix-web = "0.7" -env_logger = "0.5" +actix-web = "1.0.0-alpha.1" +env_logger = "0.6" diff --git a/cookie-auth/src/main.rs b/cookie-auth/src/main.rs index a7444793..9d1f8577 100644 --- a/cookie-auth/src/main.rs +++ b/cookie-auth/src/main.rs @@ -1,45 +1,37 @@ -extern crate actix; -extern crate actix_web; -extern crate env_logger; - -use actix_web::{middleware, server, App, HttpRequest, HttpResponse}; -use actix_web::middleware::identity::RequestIdentity; +use actix_web::middleware::identity::Identity; use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService}; +use actix_web::{middleware, web, App, HttpResponse, HttpServer}; -fn index(req: &HttpRequest) -> String { - format!("Hello {}", req.identity().unwrap_or("Anonymous".to_owned())) +fn index(id: Identity) -> String { + format!("Hello {}", id.identity().unwrap_or("Anonymous".to_owned())) } -fn login(req: &HttpRequest) -> HttpResponse { - req.remember("user1".to_owned()); +fn login(id: Identity) -> HttpResponse { + id.remember("user1".to_owned()); HttpResponse::Found().header("location", "/").finish() } -fn logout(req: &HttpRequest) -> HttpResponse { - req.forget(); +fn logout(id: Identity) -> HttpResponse { + id.forget(); HttpResponse::Found().header("location", "/").finish() } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("cookie-auth"); - server::new(|| { + HttpServer::new(|| { App::new() - .middleware(middleware::Logger::default()) - .middleware(IdentityService::new( + .wrap(middleware::Logger::default()) + .wrap(IdentityService::new( CookieIdentityPolicy::new(&[0; 32]) .name("auth-example") .secure(false), )) - .resource("/login", |r| r.f(login)) - .resource("/logout", |r| r.f(logout)) - .resource("/", |r| r.f(index)) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + .service(web::resource("/login").route(web::post().to(login))) + .service(web::resource("/logout").to(logout)) + .service(web::resource("/").route(web::get().to(index))) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/cookie-session/Cargo.toml b/cookie-session/Cargo.toml index 399a6172..ce876957 100644 --- a/cookie-session/Cargo.toml +++ b/cookie-session/Cargo.toml @@ -2,12 +2,13 @@ name = "cookie-session" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] -actix = "0.7" -actix-web = "^0.7" +actix-web = "1.0.0-alpha.1" +actix-session = "0.1.0-alpha.1" futures = "0.1" time = "0.1" -env_logger = "0.5" +env_logger = "0.6" diff --git a/cookie-session/src/main.rs b/cookie-session/src/main.rs index 94f736f9..497f01da 100644 --- a/cookie-session/src/main.rs +++ b/cookie-session/src/main.rs @@ -5,50 +5,38 @@ //! //! [User guide](https://actix.rs/book/actix-web/sec-9-middlewares.html#user-sessions) -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate futures; - -use actix_web::middleware::session::{self, RequestSession}; -use actix_web::{middleware, server, App, HttpRequest, Result}; -use std::env; +use actix_session::{CookieSession, Session}; +use actix_web::{middleware::Logger, web, App, HttpRequest, HttpServer, Result}; /// simple index handler with session -fn index(req: &HttpRequest) -> Result<&'static str> { +fn index(session: Session, req: HttpRequest) -> Result<&'static str> { println!("{:?}", req); // RequestSession trait is used for session access let mut counter = 1; - if let Some(count) = req.session().get::("counter")? { + if let Some(count) = session.get::("counter")? { println!("SESSION value: {}", count); counter = count + 1; - req.session().set("counter", counter)?; + session.set("counter", counter)?; } else { - req.session().set("counter", counter)?; + session.set("counter", counter)?; } Ok("welcome!") } -fn main() { - env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("session-example"); - server::new(|| { + HttpServer::new(|| { App::new() // enable logger - .middleware(middleware::Logger::default()) + .wrap(Logger::default()) // cookie session middleware - .middleware(session::SessionStorage::new( - session::CookieSessionBackend::signed(&[0; 32]).secure(false) - )) - .resource("/", |r| r.f(index)) - }).bind("127.0.0.1:8080") - .expect("Can not bind to 127.0.0.1:8080") - .start(); - - println!("Starting http server: 127.0.0.1:8080"); - let _ = sys.run(); + .wrap(CookieSession::signed(&[0; 32]).secure(false)) + .service(web::resource("/").to(index)) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/diesel/Cargo.toml b/diesel/Cargo.toml index a2f309b8..d8dfb918 100644 --- a/diesel/Cargo.toml +++ b/diesel/Cargo.toml @@ -2,15 +2,14 @@ name = "diesel-example" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] +actix-web = "1.0.0-alpha.1" + bytes = "0.4" -env_logger = "0.5" - -actix = "0.7" -actix-web = "0.7" - +env_logger = "0.6" futures = "0.1" uuid = { version = "0.5", features = ["serde", "v4"] } serde = "1.0" diff --git a/diesel/src/db.rs b/diesel/src/db.rs deleted file mode 100644 index de3dd179..00000000 --- a/diesel/src/db.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Db executor actor -use actix::prelude::*; -use actix_web::*; -use diesel; -use diesel::prelude::*; -use diesel::r2d2::{ConnectionManager, Pool}; -use uuid; - -use models; -use schema; - -/// This is db executor actor. We are going to run 3 of them in parallel. -pub struct DbExecutor(pub Pool>); - -/// This is only message that this actor can handle, but it is easy to extend -/// number of messages. -pub struct CreateUser { - pub name: String, -} - -impl Message for CreateUser { - type Result = Result; -} - -impl Actor for DbExecutor { - type Context = SyncContext; -} - -impl Handler for DbExecutor { - type Result = Result; - - fn handle(&mut self, msg: CreateUser, _: &mut Self::Context) -> Self::Result { - use self::schema::users::dsl::*; - - let uuid = format!("{}", uuid::Uuid::new_v4()); - let new_user = models::NewUser { - id: &uuid, - name: &msg.name, - }; - - let conn: &SqliteConnection = &self.0.get().unwrap(); - - diesel::insert_into(users) - .values(&new_user) - .execute(conn) - .map_err(|_| error::ErrorInternalServerError("Error inserting person"))?; - - let mut items = users - .filter(id.eq(&uuid)) - .load::(conn) - .map_err(|_| error::ErrorInternalServerError("Error loading person"))?; - - Ok(items.pop().unwrap()) - } -} diff --git a/diesel/src/main.rs b/diesel/src/main.rs index d30519d6..fdf3cbc4 100644 --- a/diesel/src/main.rs +++ b/diesel/src/main.rs @@ -4,77 +4,71 @@ //! Actix supports sync actors by default, so we going to create sync actor //! that use diesel. Technically sync actors are worker style actors, multiple //! of them can run in parallel and process messages from same queue. -extern crate serde; -extern crate serde_json; -#[macro_use] -extern crate serde_derive; #[macro_use] extern crate diesel; -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate futures; -extern crate r2d2; -extern crate uuid; -extern crate bytes; -// extern crate json; - +#[macro_use] +extern crate serde_derive; +use actix_web::{error, middleware, web, App, Error, HttpResponse, HttpServer}; use bytes::BytesMut; -use actix::prelude::*; -use actix_web::{ - http, middleware, server, App, AsyncResponder, FutureResponse, HttpResponse, Path, Error, HttpRequest, - State, HttpMessage, error, Json -}; - use diesel::prelude::*; -use diesel::r2d2::ConnectionManager; -use futures::{future, Future, Stream}; +use diesel::r2d2::{self, ConnectionManager}; +use futures::future::{err, Either}; +use futures::{Future, Stream}; -mod db; mod models; mod schema; -use db::{CreateUser, DbExecutor}; +type Pool = r2d2::Pool>; -/// State with DbExecutor address -struct AppState { - db: Addr, +/// Diesel query +fn query( + nm: String, + pool: web::Data, +) -> Result { + use self::schema::users::dsl::*; + + let uuid = format!("{}", uuid::Uuid::new_v4()); + let new_user = models::NewUser { + id: &uuid, + name: nm.as_str(), + }; + let conn: &SqliteConnection = &pool.get().unwrap(); + + diesel::insert_into(users).values(&new_user).execute(conn)?; + + let mut items = users.filter(id.eq(&uuid)).load::(conn)?; + Ok(items.pop().unwrap()) } /// Async request handler fn add( - (name, state): (Path, State), -) -> FutureResponse { - // send async `CreateUser` message to a `DbExecutor` - state - .db - .send(CreateUser { - name: name.into_inner(), - }) - .from_err() - .and_then(|res| match res { - Ok(user) => Ok(HttpResponse::Ok().json(user)), - Err(_) => Ok(HttpResponse::InternalServerError().into()), - }) - .responder() + name: web::Path, + pool: web::Data, +) -> impl Future { + // 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()), + }) } #[derive(Debug, Serialize, Deserialize)] struct MyUser { - name: String + name: String, } const MAX_SIZE: usize = 262_144; // max payload size is 256k /// This handler manually load request payload and parse json object -fn index_add((req, state): (HttpRequest, State)) -> impl Future { - // HttpRequest::payload() is stream of Bytes objects - req.payload() +fn index_add( + 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| { @@ -92,45 +86,39 @@ fn index_add((req, state): (HttpRequest, State)) -> impl Fut // Douman NOTE: // The return value in this closure helps, to clarify result for compiler // as otheriwse it cannot understand it - .and_then(move |body| -> Box> { + .and_then(move |body| { // body is loaded, now we can deserialize serde-json let r_obj = serde_json::from_slice::(&body); // Send to the db for create match r_obj { Ok(obj) => { - let res = state.db.send(CreateUser { name: obj.name, }) - .from_err() - .and_then(|res| match res { - Ok(user) => Ok(HttpResponse::Ok().json(user)), - Err(_) => Ok(HttpResponse::InternalServerError().into()), - }); - - Box::new(res) + 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(_) => Box::new(future::err(error::ErrorBadRequest("Json Decode Failed"))) + Err(_) => Either::B(err(error::ErrorBadRequest("Json Decode Failed"))), } }) } -fn add2((item, state): (Json, State)) -> impl Future { - state.db - .send(CreateUser { - // into_inner to move into the reference, then accessing name to - // move the name out. - name: item.into_inner().name, - }) - .from_err() - .and_then(|res| match res { - Ok(user) => Ok(HttpResponse::Ok().json(user)), - Err(_) => Ok(HttpResponse::InternalServerError().into()), - }) +fn add2( + item: web::Json, + pool: web::Data, +) -> impl Future { + // 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()), + }) } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("diesel-example"); // Start 3 db executor actors let manager = ConnectionManager::::new("test.db"); @@ -138,32 +126,40 @@ fn main() { .build(manager) .expect("Failed to create pool."); - let addr = SyncArbiter::start(3, move || DbExecutor(pool.clone())); - // Start http server - server::new(move || { - App::with_state(AppState{db: addr.clone()}) + HttpServer::new(move || { + App::new() + .data(pool.clone()) // enable logger - .middleware(middleware::Logger::default()) + .wrap(middleware::Logger::default()) // This can be called with: // curl -S --header "Content-Type: application/json" --request POST --data '{"name":"xyz"}' http://127.0.0.1:8080/add // Use of the extractors makes some post conditions simpler such // as size limit protections and built in json validation. - .resource("/add2", |r| { - r.method(http::Method::POST) - .with_async_config(add2, |(json_cfg, )| { - json_cfg.0.limit(4096); // <- limit size of the payload - }) - }) + .service( + web::resource("/add2").route( + web::post() + .data( + web::JsonConfig::default() + .limit(4096) // <- limit size of the payload + .error_handler(|err, _| { + // <- create custom error response + error::InternalError::from_response( + err, + HttpResponse::Conflict().finish(), + ) + .into() + }), + ) + .to_async(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. - .resource("/add", |r| r.method(http::Method::POST).with_async(index_add)) - .resource("/add/{name}", |r| r.method(http::Method::GET).with(add)) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + .service(web::resource("/add").route(web::post().to_async(index_add))) + .service(web::resource("/add/{name}").route(web::get().to_async(add))) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/error_handling/Cargo.toml b/error_handling/Cargo.toml index fe27b1c3..7e05407c 100644 --- a/error_handling/Cargo.toml +++ b/error_handling/Cargo.toml @@ -2,11 +2,13 @@ name = "error_handling" version = "0.1.0" authors = ["dowwie "] +edition = "2018" +workspace = ".." [dependencies] -actix = "0.7.3" -actix-web = "0.7.3" -failure = "0.1.2" +actix-web = "1.0.0-alpha.1" + +derive_more = "0.14.0" futures = "0.1.23" rand = "0.5.4" -env_logger = "0.5.12" +env_logger = "0.6" diff --git a/error_handling/src/main.rs b/error_handling/src/main.rs index 6a1788f0..676a8ce8 100644 --- a/error_handling/src/main.rs +++ b/error_handling/src/main.rs @@ -1,10 +1,10 @@ /* -The goal of this example is to show how to propagate a custom error type, derived -from the Fail trait, to a web handler that will evaluate the type of error that +The goal of this example is to show how to propagate a custom error type, +to a web handler that will evaluate the type of error that was raised and return an appropriate HTTPResponse. This example uses a 50/50 chance of returning 200 Ok, otherwise one of four possible -http errors will be chosen, each with an equal chance of being selected: +http errors will be chosen, each with an equal chance of being selected: 1. 403 Forbidden 2. 401 Unauthorized 3. 500 InternalServerError @@ -12,128 +12,91 @@ http errors will be chosen, each with an equal chance of being selected: */ - -extern crate actix; -extern crate actix_web; -extern crate env_logger; -#[macro_use] extern crate failure; -extern crate futures; -extern crate rand; - - -use actix_web::{ - http::Method, server, App, AsyncResponder, Error as ActixWebError, - HttpResponse, HttpRequest +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, }; -use failure::Error as FailureError; // naming it clearly for illustration purposes -use futures::{ - future::{ - ok as fut_ok, - err as fut_err - }, - Future -}; -use rand::{thread_rng, Rng, distributions::{Distribution, Standard}}; - - -#[derive(Fail, Debug)] +#[derive(Debug, Display)] pub enum CustomError { - #[fail(display = "Custom Error 1")] + #[display(fmt = "Custom Error 1")] CustomOne, - #[fail(display = "Custom Error 2")] + #[display(fmt = "Custom Error 2")] CustomTwo, - #[fail(display = "Custom Error 3")] + #[display(fmt = "Custom Error 3")] CustomThree, - #[fail(display = "Custom Error 4")] - CustomFour + #[display(fmt = "Custom Error 4")] + CustomFour, } - impl Distribution for Standard { fn sample(&self, rng: &mut R) -> CustomError { match rng.gen_range(0, 4) { 0 => CustomError::CustomOne, 1 => CustomError::CustomTwo, 2 => CustomError::CustomThree, - _ => CustomError::CustomFour + _ => CustomError::CustomFour, } } } -/* +/// Actix web uses `ResponseError` for conversion of errors to a response impl ResponseError for CustomError { fn error_response(&self) -> HttpResponse { - HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR) + match self { + CustomError::CustomOne => { + println!("do some stuff related to CustomOne error"); + HttpResponse::Forbidden().finish() + } + + CustomError::CustomTwo => { + println!("do some stuff related to CustomTwo error"); + HttpResponse::Unauthorized().finish() + } + + CustomError::CustomThree => { + println!("do some stuff related to CustomThree error"); + HttpResponse::InternalServerError().finish() + } + + _ => { + println!("do some stuff related to CustomFour error"); + HttpResponse::BadRequest().finish() + } + } } } -*/ - /// randomly returns either () or one of the 4 CustomError variants -//fn do_something_random() -> impl Future, -// Error = ActixWebError> { fn do_something_random() -> impl Future { let mut rng = thread_rng(); // 20% chance that () will be returned by this function - if rng.gen_bool(2.0/10.0) { - return fut_ok(()) + if rng.gen_bool(2.0 / 10.0) { + ok(()) + } else { + err(rand::random::()) } - - let err: CustomError = rand::random(); - return fut_err(err) } - -fn do_something(_req: HttpRequest) - -> impl Future { - - do_something_random() - .then(|result| match result { - Ok(_) => Ok(HttpResponse::Ok() - .body("Nothing interesting happened. Try again.")), - - Err(err) => match err { - CustomError::CustomOne => { - println!("do some stuff related to CustomOne error"); - Ok(HttpResponse::Forbidden().finish()) - }, - - CustomError::CustomTwo => { - println!("do some stuff related to CustomTwo error"); - Ok(HttpResponse::Unauthorized().finish()) - }, - - CustomError::CustomThree => { - println!("do some stuff related to CustomThree error"); - Ok(HttpResponse::InternalServerError().finish()) - }, - - _ => { - println!("do some stuff related to CustomFour error"); - Ok(HttpResponse::BadRequest().finish()) - } - } +fn do_something() -> impl Future { + do_something_random().from_err().and_then(|_| { + HttpResponse::Ok().body("Nothing interesting happened. Try again.") }) - .responder() } - -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("error_handling_example"); - server::new(move || { - App::new() - .resource("/something", |r| - r.method(Method::GET) - .with_async(do_something)) - }).bind("127.0.0.1:8088") - .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8088"); - let _ = sys.run(); + HttpServer::new(move || { + App::new().service( + web::resource("/something").route(web::get().to_async(do_something)), + ) + }) + .bind("127.0.0.1:8088")? + .run() } diff --git a/form/Cargo.toml b/form/Cargo.toml index 42c314ff..150b9417 100644 --- a/form/Cargo.toml +++ b/form/Cargo.toml @@ -2,10 +2,11 @@ name = "form-example" version = "0.1.0" authors = ["Gorm Casper "] +edition = "2018" +workspace = ".." [dependencies] -actix = "0.7" -actix-web = "0.7" +actix-web = "1.0.0-alpha.1" serde = "1.0" serde_derive = "1.0" diff --git a/form/src/main.rs b/form/src/main.rs index 615fff2e..ebdddf0a 100644 --- a/form/src/main.rs +++ b/form/src/main.rs @@ -1,46 +1,32 @@ -extern crate actix; -extern crate actix_web; - #[macro_use] extern crate serde_derive; use actix_web::{ - http, middleware, server, App, Form, HttpRequest, HttpResponse, Result, State, + middleware, web, App, HttpRequest, HttpResponse, HttpServer, Responder, Result, }; struct AppState { foo: String, } -fn main() { - let sys = actix::System::new("form-example"); - - let _addr = server::new(|| { - App::with_state(AppState { - foo: "bar".to_string(), - }).middleware(middleware::Logger::default()) - .resource("/", |r| { - r.method(http::Method::GET).with(index); +fn main() -> std::io::Result<()> { + HttpServer::new(|| { + App::new() + .data(AppState { + foo: "bar".to_string(), }) - .resource("/post1", |r| { - r.method(http::Method::POST).with(handle_post_1) - }) - .resource("/post2", |r| { - r.method(http::Method::POST).with(handle_post_2) - }) - .resource("/post3", |r| { - r.method(http::Method::POST).with(handle_post_3) - }) - }).bind("127.0.0.1:8080") - .expect("Can not bind to 127.0.0.1:8080") - .start(); - - println!("Starting http server: 127.0.0.1:8080"); - let _ = sys.run(); + .wrap(middleware::Logger::default()) + .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))) + }) + .bind("127.0.0.1:8080")? + .run() } -fn index(_req: HttpRequest) -> Result { - Ok(HttpResponse::build(http::StatusCode::OK) +fn index() -> Result { + Ok(HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(include_str!("../static/form.html"))) } @@ -51,30 +37,28 @@ pub struct MyParams { } /// Simple handle POST request -fn handle_post_1(params: Form) -> Result { - Ok(HttpResponse::build(http::StatusCode::OK) +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( - (state, params): (State, Form), -) -> Result { - Ok(HttpResponse::build(http::StatusCode::OK) - .content_type("text/plain") - .body(format!( - "Your name is {}, and in AppState I have foo: {}", - params.name, state.foo - ))) + state: web::Data, + params: web::Form, +) -> HttpResponse { + HttpResponse::Ok().content_type("text/plain").body(format!( + "Your name is {}, and in AppState I have foo: {}", + params.name, state.foo + )) } /// Request and POST Params -fn handle_post_3( - (req, params): (HttpRequest, Form), -) -> Result { +fn handle_post_3(req: HttpRequest, params: web::Form) -> impl Responder { println!("Handling POST request: {:?}", req); - Ok(HttpResponse::build(http::StatusCode::OK) + + HttpResponse::Ok() .content_type("text/plain") - .body(format!("Your name is {}", params.name))) + .body(format!("Your name is {}", params.name)) } diff --git a/hello-world/Cargo.toml b/hello-world/Cargo.toml index 7126813c..502b1410 100644 --- a/hello-world/Cargo.toml +++ b/hello-world/Cargo.toml @@ -2,10 +2,9 @@ name = "hello-world" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] -env_logger = "0.5" - -actix = "0.7" -actix-web = "^0.7" +actix-web = "1.0.0-alpha.1" +env_logger = "0.6" diff --git a/hello-world/src/main.rs b/hello-world/src/main.rs index 90d93b7d..ac294063 100644 --- a/hello-world/src/main.rs +++ b/hello-world/src/main.rs @@ -1,28 +1,21 @@ -extern crate actix; -extern crate actix_web; -extern crate env_logger; +use actix_web::{middleware, web, App, HttpRequest, HttpServer}; -use actix_web::{middleware, server, App, HttpRequest}; - -fn index(_req: &HttpRequest) -> &'static str { +fn index(req: HttpRequest) -> &'static str { + println!("REQ: {:?}", req); "Hello world!" } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("hello-world"); - server::new(|| { + HttpServer::new(|| { App::new() // enable logger - .middleware(middleware::Logger::default()) - .resource("/index.html", |r| r.f(|_| "Hello world!")) - .resource("/", |r| r.f(index)) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + .wrap(middleware::Logger::default()) + .service(web::resource("/index.html").to(|| "Hello world!")) + .service(web::resource("/").to(index)) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/http-full-proxy/Cargo.toml b/http-full-proxy/Cargo.toml index f9363344..f906fdd2 100644 --- a/http-full-proxy/Cargo.toml +++ b/http-full-proxy/Cargo.toml @@ -2,10 +2,13 @@ name = "http-full-proxy" version = "0.1.0" authors = ["Rotem Yaari"] +workspace = ".." +edition = "2018" [dependencies] -actix = "0.7.5" -actix-web = "0.7.13" +actix-rt = "0.2" +actix-web = "1.0.0-alpha.1" + clap = "2.32.0" futures = "0.1.25" failure = "0.1.3" diff --git a/http-full-proxy/src/main.rs b/http-full-proxy/src/main.rs index 4d691870..f350769e 100644 --- a/http-full-proxy/src/main.rs +++ b/http-full-proxy/src/main.rs @@ -1,85 +1,53 @@ -#![deny(warnings)] -extern crate actix; -extern crate actix_web; -extern crate clap; -extern crate failure; -extern crate futures; -extern crate url; - -use actix_web::{ - client, http, server, App, AsyncResponder, Error, HttpMessage, HttpRequest, - HttpResponse, -}; +use actix_web::client::Client; +use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; use clap::{value_t, Arg}; -use futures::{future, Future}; +use futures::Future; use std::net::ToSocketAddrs; use url::Url; -struct AppState { - forward_url: Url, -} - -impl AppState { - pub fn init(forward_url: Url) -> AppState { - AppState { forward_url } - } -} - fn forward( - req: &HttpRequest, -) -> Box> { - let mut new_url = req.state().forward_url.clone(); + req: HttpRequest, + payload: web::Payload, + url: web::Data, + client: web::Data, +) -> impl Future { + let mut new_url = url.get_ref().clone(); new_url.set_path(req.uri().path()); new_url.set_query(req.uri().query()); - let mut forwarded_req = client::ClientRequest::build_from(req) - .no_default_headers() - .uri(new_url) - .streaming(req.payload()) - .unwrap(); + let forwarded_req = client + .request_from(new_url.as_str(), &req) + .no_default_headers(); - if let Some(addr) = req.peer_addr() { - match forwarded_req.headers_mut().entry("x-forwarded-for") { - Ok(http::header::Entry::Vacant(entry)) => { - let addr = format!("{}", addr.ip()); - entry.insert(addr.parse().unwrap()); - } - Ok(http::header::Entry::Occupied(mut entry)) => { - let addr = format!("{}, {}", entry.get().to_str().unwrap(), addr.ip()); - entry.insert(addr.parse().unwrap()); - } - _ => unreachable!(), - } - } + // if let Some(addr) = req.peer_addr() { + // match forwarded_req.headers_mut().entry("x-forwarded-for") { + // Ok(http::header::Entry::Vacant(entry)) => { + // let addr = format!("{}", addr.ip()); + // entry.insert(addr.parse().unwrap()); + // } + // Ok(http::header::Entry::Occupied(mut entry)) => { + // let addr = format!("{}, {}", entry.get().to_str().unwrap(), addr.ip()); + // entry.insert(addr.parse().unwrap()); + // } + // _ => unreachable!(), + // } + // } forwarded_req - .send() + .send_stream(payload) .map_err(Error::from) - .and_then(construct_response) - .responder() + .map(|res| { + let mut client_resp = HttpResponse::build(res.status()); + for (header_name, header_value) in + res.headers().iter().filter(|(h, _)| *h != "connection") + { + client_resp.header(header_name.clone(), header_value.clone()); + } + client_resp.streaming(res) + }) } -fn construct_response( - resp: client::ClientResponse, -) -> Box> { - let mut client_resp = HttpResponse::build(resp.status()); - for (header_name, header_value) in - resp.headers().iter().filter(|(h, _)| *h != "connection") - { - client_resp.header(header_name.clone(), header_value.clone()); - } - if resp.chunked().unwrap_or(false) { - Box::new(future::ok(client_resp.streaming(resp.payload()))) - } else { - Box::new( - resp.body() - .from_err() - .and_then(move |body| Ok(client_resp.body(body))), - ) - } -} - -fn main() { +fn main() -> std::io::Result<()> { let matches = clap::App::new("HTTP Proxy") .arg( Arg::with_name("listen_addr") @@ -87,25 +55,29 @@ fn main() { .value_name("LISTEN ADDR") .index(1) .required(true), - ).arg( + ) + .arg( Arg::with_name("listen_port") .takes_value(true) .value_name("LISTEN PORT") .index(2) .required(true), - ).arg( + ) + .arg( Arg::with_name("forward_addr") .takes_value(true) .value_name("FWD ADDR") .index(3) .required(true), - ).arg( + ) + .arg( Arg::with_name("forward_port") .takes_value(true) .value_name("FWD PORT") .index(4) .required(true), - ).get_matches(); + ) + .get_matches(); let listen_addr = matches.value_of("listen_addr").unwrap(); let listen_port = value_t!(matches, "listen_port", u16).unwrap_or_else(|e| e.exit()); @@ -121,15 +93,16 @@ fn main() { .unwrap() .next() .unwrap() - )).unwrap(); + )) + .unwrap(); - server::new(move || { - App::with_state(AppState::init(forward_url.clone())).default_resource(|r| { - r.f(forward); - }) - }).workers(32) - .bind((listen_addr, listen_port)) - .expect("Cannot bind listening port") + HttpServer::new(move || { + App::new() + .data(forward_url.clone()) + .wrap(middleware::Logger::default()) + .default_resource(|r| r.to_async(forward)) + }) + .bind((listen_addr, listen_port))? .system_exit() - .run(); + .run() } diff --git a/http-proxy/Cargo.toml b/http-proxy/Cargo.toml index b06a5946..487d4580 100644 --- a/http-proxy/Cargo.toml +++ b/http-proxy/Cargo.toml @@ -2,7 +2,8 @@ name = "http-proxy" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +edition = "2018" +workspace = ".." [[bin]] name = "proxy" @@ -13,8 +14,8 @@ name = "proxy-example-server" path = "src/server.rs" [dependencies] +actix-rt = "0.2" +actix-web = "1.0.0-alpha.1" + env_logger = "0.5" futures = "0.1" - -actix = "0.7" -actix-web = "^0.7" diff --git a/http-proxy/src/main.rs b/http-proxy/src/main.rs index 3785ae6c..080c848d 100644 --- a/http-proxy/src/main.rs +++ b/http-proxy/src/main.rs @@ -1,60 +1,52 @@ -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate futures; - -use actix_web::{ - client, middleware, server, App, AsyncResponder, Body, Error, HttpMessage, - HttpRequest, HttpResponse, -}; -use futures::{Future, Stream}; +use actix_web::client::Client; +use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer}; +use futures::Future; /// Stream client request response and then send body to a server response -fn index(_req: &HttpRequest) -> Box> { - client::ClientRequest::get("http://127.0.0.1:8081/") - .finish().unwrap() +fn index(client: web::Data) -> impl Future { + client + .get("http://127.0.0.1:8081/") .send() - .map_err(Error::from) // <- convert SendRequestError to an Error - .and_then( - |resp| resp.body() // <- this is MessageBody type, resolves to complete body - .from_err() // <- convert PayloadError to an Error - .and_then(|body| { // <- we got complete body, now send as server response + .map_err(Error::from) // <- convert SendRequestError to an Error + .and_then(|resp| { + resp.body() // <- this is MessageBody type, resolves to complete body + .from_err() // <- convert PayloadError to an Error + .and_then(|body| { + // <- we got complete body, now send as server response Ok(HttpResponse::Ok().body(body)) - })) - .responder() + }) + }) } /// streaming client request to a streaming server response -fn streaming(_req: &HttpRequest) -> Box> { +fn streaming( + client: web::Data, +) -> impl Future> { // send client request - client::ClientRequest::get("https://www.rust-lang.org/en-US/") - .finish().unwrap() - .send() // <- connect to host and send request - .map_err(Error::from) // <- convert SendRequestError to an Error - .and_then(|resp| { // <- we received client response + client + .get("https://www.rust-lang.org/en-US/") + .send() // <- connect to host and send request + .map_err(Error::from) // <- convert SendRequestError to an Error + .and_then(|resp| { + // <- we received client response Ok(HttpResponse::Ok() - // read one chunk from client response and send this chunk to a server response - // .from_err() converts PayloadError to an Error - .body(Body::Streaming(Box::new(resp.payload().from_err())))) + // read one chunk from client response and send this chunk to a server response + // .from_err() converts PayloadError to an Error + .streaming(resp)) }) - .responder() } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_server=info,actix_web=trace"); env_logger::init(); - let sys = actix::System::new("http-proxy"); - server::new(|| { + HttpServer::new(|| { App::new() - .middleware(middleware::Logger::default()) - .resource("/streaming", |r| r.f(streaming)) - .resource("/", |r| r.f(index)) - }).workers(1) - .bind("127.0.0.1:8080") - .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + .data(Client::new()) + .wrap(middleware::Logger::default()) + .service(web::resource("/streaming").to_async(streaming)) + .service(web::resource("/").to_async(index)) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/http-proxy/src/server.rs b/http-proxy/src/server.rs index ca1dfb26..a8e7559e 100644 --- a/http-proxy/src/server.rs +++ b/http-proxy/src/server.rs @@ -1,34 +1,20 @@ -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate futures; +use actix_web::{middleware, web, App, HttpResponse, HttpServer, Responder}; -use actix_web::*; -use futures::Future; - -fn index(req: &HttpRequest) -> FutureResponse { - req.body() - .from_err() - .map(|bytes| HttpResponse::Ok().body(bytes)) - .responder() +fn index(body: web::Bytes) -> impl Responder { + HttpResponse::Ok().body(body) } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=error"); - let _ = env_logger::init(); - let sys = actix::System::new("ws-example"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_server=info,actix_web=trace"); + env_logger::init(); - server::new(|| { + HttpServer::new(|| { App::new() - // enable logger - .middleware(middleware::Logger::default()) - .resource("/index.html", |r| r.f(|_| "Hello world!")) - .resource("/", |r| r.f(index)) - }).workers(1) - .bind("127.0.0.1:8081") - .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8081"); - let _ = sys.run(); + // enable logger + .wrap(middleware::Logger::default()) + .service(web::resource("/index.html").to(|| "Hello world!")) + .service(web::resource("/").to(index)) + }) + .bind("127.0.0.1:8081")? + .run() } diff --git a/json/Cargo.toml b/json/Cargo.toml index c358f4ae..82b525c8 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -2,9 +2,12 @@ name = "json-example" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] +actix-web = "1.0.0-alpha.1" + bytes = "0.4" futures = "0.1" env_logger = "*" @@ -13,6 +16,3 @@ serde = "1.0" serde_json = "1.0" serde_derive = "1.0" json = "*" - -actix = "0.7" -actix-web = "^0.7" diff --git a/json/src/main.rs b/json/src/main.rs index a93f761e..9b0d9a57 100644 --- a/json/src/main.rs +++ b/json/src/main.rs @@ -1,22 +1,13 @@ -extern crate actix; -extern crate actix_web; -extern crate bytes; -extern crate env_logger; -extern crate futures; -extern crate serde_json; -#[macro_use] -extern crate serde_derive; #[macro_use] extern crate json; use actix_web::{ - error, http, middleware, server, App, AsyncResponder, Error, HttpMessage, - HttpRequest, HttpResponse, Json, + error, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, }; - use bytes::BytesMut; use futures::{Future, Stream}; use json::JsonValue; +use serde_derive::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] struct MyObj { @@ -24,39 +15,31 @@ struct MyObj { number: i32, } -/// This handler uses `HttpRequest::json()` for loading json object. -fn index(req: &HttpRequest) -> Box> { - req.json() - .from_err() // convert all errors into `Error` - .and_then(|val: MyObj| { - println!("model: {:?}", val); - Ok(HttpResponse::Ok().json(val)) // <- send response - }) - .responder() -} - /// This handler uses json extractor -fn extract_item(item: Json) -> HttpResponse { +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_limit((item, _req): (Json, HttpRequest)) -> HttpResponse { - println!("model: {:?}", &item); - HttpResponse::Ok().json(item.0) // <- send response +fn extract_item(item: web::Json, req: HttpRequest) -> HttpResponse { + println!("request: {:?}", req); + println!("model: {:?}", item); + + HttpResponse::Ok().json(item.0) // <- send json response } const MAX_SIZE: usize = 262_144; // max payload size is 256k /// This handler manually load request payload and parse json object -fn index_manual(req: &HttpRequest) -> Box> { - // HttpRequest::payload() is stream of Bytes objects - req.payload() +fn index_manual( + payload: web::Payload, +) -> impl Future { + // 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| { @@ -75,59 +58,51 @@ fn index_manual(req: &HttpRequest) -> Box(&body)?; Ok(HttpResponse::Ok().json(obj)) // <- send response }) - .responder() } /// This handler manually load request payload and parse json-rust -fn index_mjsonrust( - req: &HttpRequest, -) -> Box> { - req.payload() - .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) => object!{"err" => e.to_string() }, - }; - Ok(HttpResponse::Ok() - .content_type("application/json") - .body(injson.dump())) - }) - .responder() +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())) + }) } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("json-example"); - server::new(|| { + HttpServer::new(|| { App::new() // enable logger - .middleware(middleware::Logger::default()) - .resource("/extractor", |r| { - r.method(http::Method::POST) - .with_config(extract_item, |(cfg,)| { - cfg.limit(4096); // <- limit size of the payload - }) - }) - .resource("/extractor2", |r| { - r.method(http::Method::POST) - .with_config(extract_item_limit, |((cfg, _),)| { - cfg.limit(4096); // <- limit size of the payload - }) - }) - .resource("/manual", |r| r.method(http::Method::POST).f(index_manual)) - .resource("/mjsonrust", |r| r.method(http::Method::POST).f(index_mjsonrust)) - .resource("/", |r| r.method(http::Method::POST).f(index)) - }).bind("127.0.0.1:8080") - .unwrap() - .shutdown_timeout(1) - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + .wrap(middleware::Logger::default()) + .service( + web::resource("/extractor").route( + web::post() + .data(web::JsonConfig::default().limit(4096)) // <- limit size of the payload + .to(index), + ), + ) + .service( + web::resource("/extractor2").route( + web::post() + .data(web::JsonConfig::default().limit(4096)) // <- limit size of the payload + .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)), + ) + .service(web::resource("/").route(web::post().to(index))) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml index d19b3527..ff47263a 100644 --- a/juniper/Cargo.toml +++ b/juniper/Cargo.toml @@ -2,17 +2,14 @@ name = "juniper-example" version = "0.1.0" authors = ["pyros2097 "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] -env_logger = "0.5" - -actix = "0.7" -actix-web = "0.7" - +actix-web = "1.0.0-alpha.1" +env_logger = "0.6" futures = "0.1" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" - juniper = "0.9.2" diff --git a/juniper/src/main.rs b/juniper/src/main.rs index d06cc5e7..2615d34e 100644 --- a/juniper/src/main.rs +++ b/juniper/src/main.rs @@ -1,107 +1,59 @@ //! Actix web juniper example //! //! A simple example integrating juniper in actix-web -extern crate serde; -extern crate serde_json; -#[macro_use] -extern crate serde_derive; +use std::io; +use std::sync::Arc; + #[macro_use] extern crate juniper; -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate futures; -use actix::prelude::*; -use actix_web::{ - http, middleware, server, App, AsyncResponder, Error, FutureResponse, HttpRequest, - HttpResponse, Json, State, -}; +use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer}; use futures::future::Future; use juniper::http::graphiql::graphiql_source; use juniper::http::GraphQLRequest; mod schema; -use schema::create_schema; -use schema::Schema; +use crate::schema::{create_schema, Schema}; -struct AppState { - executor: Addr, -} - -#[derive(Serialize, Deserialize)] -pub struct GraphQLData(GraphQLRequest); - -impl Message for GraphQLData { - type Result = Result; -} - -pub struct GraphQLExecutor { - schema: std::sync::Arc, -} - -impl GraphQLExecutor { - fn new(schema: std::sync::Arc) -> GraphQLExecutor { - GraphQLExecutor { schema: schema } - } -} - -impl Actor for GraphQLExecutor { - type Context = SyncContext; -} - -impl Handler for GraphQLExecutor { - type Result = Result; - - fn handle(&mut self, msg: GraphQLData, _: &mut Self::Context) -> Self::Result { - let res = msg.0.execute(&self.schema, &()); - let res_text = serde_json::to_string(&res)?; - Ok(res_text) - } -} - -fn graphiql(_req: &HttpRequest) -> Result { +fn graphiql() -> HttpResponse { let html = graphiql_source("http://127.0.0.1:8080/graphql"); - Ok(HttpResponse::Ok() + HttpResponse::Ok() .content_type("text/html; charset=utf-8") - .body(html)) + .body(html) } fn graphql( - (st, data): (State, Json), -) -> FutureResponse { - st.executor - .send(data.0) - .from_err() - .and_then(|res| match res { - Ok(user) => Ok(HttpResponse::Ok() - .content_type("application/json") - .body(user)), - Err(_) => Ok(HttpResponse::InternalServerError().into()), - }) - .responder() + st: web::Data>, + data: web::Json, +) -> impl Future { + web::block(move || { + let res = data.execute(&st, &()); + Ok::<_, serde_json::error::Error>(serde_json::to_string(&res)?) + }) + .map_err(Error::from) + .and_then(|user| { + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(user)) + }) } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("juniper-example"); + // Create Juniper schema let schema = std::sync::Arc::new(create_schema()); - let addr = SyncArbiter::start(3, move || GraphQLExecutor::new(schema.clone())); // Start http server - server::new(move || { - App::with_state(AppState{executor: addr.clone()}) - // enable logger - .middleware(middleware::Logger::default()) - .resource("/graphql", |r| r.method(http::Method::POST).with(graphql)) - .resource("/graphiql", |r| r.method(http::Method::GET).h(graphiql)) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + HttpServer::new(move || { + App::new() + .data(schema.clone()) + .wrap(middleware::Logger::default()) + .service(web::resource("/graphql").route(web::post().to_async(graphql))) + .service(web::resource("/graphiql").route(web::get().to(graphiql))) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/middleware/Cargo.toml b/middleware/Cargo.toml index b14354f4..c5657087 100644 --- a/middleware/Cargo.toml +++ b/middleware/Cargo.toml @@ -2,7 +2,11 @@ name = "middleware-example" version = "0.1.0" authors = ["Gorm Casper "] +edition = "2018" +workspace = ".." [dependencies] -actix = "0.7" -actix-web = "0.7" +actix-service = "0.3.3" +actix-web = "1.0.0-alpha.1" +futures = "0.1.25" +env_logger = "0.6" \ No newline at end of file diff --git a/middleware/src/main.rs b/middleware/src/main.rs index 539a3076..0496db27 100644 --- a/middleware/src/main.rs +++ b/middleware/src/main.rs @@ -1,29 +1,27 @@ -extern crate actix; -extern crate actix_web; - -use actix_web::{server, App}; +use actix_web::{web, App, HttpServer}; #[allow(dead_code)] mod redirect; #[allow(dead_code)] mod simple; -fn main() { - let sys = actix::System::new("middleware-example"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=debug"); + env_logger::init(); - let _addr = server::new(|| { + HttpServer::new(|| { App::new() - .middleware(simple::SayHi) - // .middleware(redirect::CheckLogin) - .resource("/login", |r| { - r.f(|_| "You are on /login. Go to src/redirect.rs to change this behavior.") - }) - .resource("/", |r| { - r.f(|_| "Hello, middleware! Check the console where the server is run.") - }) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - - let _ = sys.run(); + .wrap(redirect::CheckLogin) + .wrap(simple::SayHi) + .service(web::resource("/login").to(|| { + "You are on /login. Go to src/redirect.rs to change this behavior." + })) + .service( + web::resource("/").to(|| { + "Hello, middleware! Check the console where the server is run." + }), + ) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/middleware/src/redirect.rs b/middleware/src/redirect.rs index a0d5de6c..3ffd3a87 100644 --- a/middleware/src/redirect.rs +++ b/middleware/src/redirect.rs @@ -1,28 +1,64 @@ -extern crate actix_web; - -use actix_web::middleware::{Middleware, Started}; -use actix_web::{http, HttpRequest, HttpResponse, Result}; +use actix_service::{Service, Transform}; +use actix_web::dev::{ServiceRequest, ServiceResponse}; +use actix_web::{http, HttpResponse}; +use futures::future::{ok, Either, FutureResult}; +use futures::Poll; pub struct CheckLogin; -impl Middleware for CheckLogin { - // We only need to hook into the `start` for this middleware. - fn start(&self, req: &HttpRequest) -> Result { +impl Transform for CheckLogin +where + S: Service, Response = ServiceResponse>, + S::Future: 'static, +{ + type Request = ServiceRequest

; + type Response = ServiceResponse; + type Error = S::Error; + type InitError = (); + type Transform = CheckLoginMiddleware; + type Future = FutureResult; + + fn new_transform(&self, service: S) -> Self::Future { + ok(CheckLoginMiddleware { service }) + } +} +pub struct CheckLoginMiddleware { + service: S, +} + +impl Service for CheckLoginMiddleware +where + S: Service, Response = ServiceResponse>, + S::Future: 'static, +{ + type Request = ServiceRequest

; + type Response = ServiceResponse; + type Error = S::Error; + type Future = Either>; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + self.service.poll_ready() + } + + fn call(&mut self, req: ServiceRequest

) -> Self::Future { + // We only need to hook into the `start` for this middleware. + let is_logged_in = false; // Change this to see the change in outcome in the browser if is_logged_in { - return Ok(Started::Done); + Either::A(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)) + } else { + Either::B(ok(req.into_response( + HttpResponse::Found() + .header(http::header::LOCATION, "/login") + .finish() + .into_body(), + ))) + } } - - // Don't forward to /login if we are already on /login - if req.path() == "/login" { - return Ok(Started::Done); - } - - Ok(Started::Response( - HttpResponse::Found() - .header(http::header::LOCATION, "/login") - .finish(), - )) } } diff --git a/middleware/src/simple.rs b/middleware/src/simple.rs index 254f263d..68f6e143 100644 --- a/middleware/src/simple.rs +++ b/middleware/src/simple.rs @@ -1,25 +1,63 @@ -extern crate actix_web; +use actix_service::{Service, Transform}; +use actix_web::dev::{ServiceRequest, ServiceResponse}; +use futures::future::{ok, FutureResult}; +use futures::{Future, Poll}; -use actix_web::middleware::{Finished, Middleware, Response, Started}; -use actix_web::{HttpRequest, HttpResponse, Result}; - -// Middleware can get called at three stages during the request/response handling. Below is a -// struct that implements all three of them. +// There are two step in middleware processing. +// 1. Middleware initialization, middleware factory get called with +// next service in chain as parameter. +// 2. Middleware's call method get called with normal request. pub struct SayHi; -impl Middleware for SayHi { - fn start(&self, req: &HttpRequest) -> Result { - println!("Hi from start. You requested: {}", req.path()); - Ok(Started::Done) - } +// Middleware factory is `Transform` trait from actix-service crate +// `S` - type of the next service +// `P` - type of request's payload +// `B` - type of response's body +impl Transform for SayHi +where + S: Service, Response = ServiceResponse>, + S::Future: 'static, + S::Error: 'static, + B: 'static, +{ + type Request = ServiceRequest

; + type Response = ServiceResponse; + type Error = S::Error; + type InitError = (); + type Transform = SayHiMiddleware; + type Future = FutureResult; - fn response(&self, _req: &HttpRequest, resp: HttpResponse) -> Result { - println!("Hi from response"); - Ok(Response::Done(resp)) - } - - fn finish(&self, _req: &HttpRequest, _resp: &HttpResponse) -> Finished { - println!("Hi from finish"); - Finished::Done + fn new_transform(&self, service: S) -> Self::Future { + ok(SayHiMiddleware { service }) + } +} + +pub struct SayHiMiddleware { + service: S, +} + +impl Service for SayHiMiddleware +where + S: Service, Response = ServiceResponse>, + S::Future: 'static, + S::Error: 'static, + B: 'static, +{ + type Request = ServiceRequest

; + type Response = ServiceResponse; + type Error = S::Error; + type Future = Box>; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + self.service.poll_ready() + } + + 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| { + println!("Hi from response"); + Ok(res) + })) } } diff --git a/multipart/Cargo.toml b/multipart/Cargo.toml index 771e28f7..994c80fc 100644 --- a/multipart/Cargo.toml +++ b/multipart/Cargo.toml @@ -2,15 +2,16 @@ name = "multipart-example" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [[bin]] name = "multipart" path = "src/main.rs" [dependencies] -env_logger = "*" -futures = "0.1" +#actix-web = "1.0.0-alpha.1" +actix-web = { git="https://github.com/actix/actix-web.git" } -actix = "0.7" -actix-web = "0.7" +env_logger = "0.6" +futures = "0.1.25" diff --git a/multipart/src/main.rs b/multipart/src/main.rs index 6cea2536..40f22d38 100644 --- a/multipart/src/main.rs +++ b/multipart/src/main.rs @@ -1,44 +1,30 @@ -#![allow(unused_variables)] -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate futures; - use std::cell::Cell; use std::fs; use std::io::Write; -use actix_web::{ - dev, error, http, middleware, multipart, server, App, Error, FutureResponse, - HttpMessage, HttpRequest, HttpResponse, -}; - -use futures::future; +use actix_web::{error, middleware, web, App, Error, HttpResponse, HttpServer}; +use futures::future::{err, Either}; use futures::{Future, Stream}; pub struct AppState { pub counter: Cell, } -pub fn save_file( - field: multipart::Field, -) -> Box> { +pub fn save_file(field: web::MultipartField) -> impl Future { let file_path_string = "upload.png"; let mut file = match fs::File::create(file_path_string) { Ok(file) => file, - Err(e) => return Box::new(future::err(error::ErrorInternalServerError(e))), + Err(e) => return Either::A(err(error::ErrorInternalServerError(e))), }; - Box::new( + Either::B( field .fold(0i64, move |acc, bytes| { - let rt = file - .write_all(bytes.as_ref()) + file.write_all(bytes.as_ref()) .map(|_| acc + bytes.len() as i64) .map_err(|e| { println!("file.write_all failed: {:?}", e); error::MultipartError::Payload(error::PayloadError::Io(e)) - }); - future::result(rt) + }) }) .map_err(|e| { println!("save_file failed, {:?}", e); @@ -48,13 +34,11 @@ pub fn save_file( } pub fn handle_multipart_item( - item: multipart::MultipartItem, + item: web::MultipartItem, ) -> Box> { match item { - multipart::MultipartItem::Field(field) => { - Box::new(save_file(field).into_stream()) - } - multipart::MultipartItem::Nested(mp) => Box::new( + web::MultipartItem::Field(field) => Box::new(save_file(field).into_stream()), + web::MultipartItem::Nested(mp) => Box::new( mp.map_err(error::ErrorInternalServerError) .map(handle_multipart_item) .flatten(), @@ -62,24 +46,26 @@ pub fn handle_multipart_item( } } -pub fn upload(req: HttpRequest) -> FutureResponse { - req.state().counter.set(req.state().counter.get() + 1); - println!("{:?}", req.state().counter.get()); - Box::new( - req.multipart() - .map_err(error::ErrorInternalServerError) - .map(handle_multipart_item) - .flatten() - .collect() - .map(|sizes| HttpResponse::Ok().json(sizes)) - .map_err(|e| { - println!("failed: {}", e); - e - }), - ) +pub fn upload( + multipart: web::Multipart, + counter: web::Data>, +) -> impl Future { + counter.set(counter.get() + 1); + println!("{:?}", counter.get()); + + multipart + .map_err(error::ErrorInternalServerError) + .map(handle_multipart_item) + .flatten() + .collect() + .map(|sizes| HttpResponse::Ok().json(sizes)) + .map_err(|e| { + println!("failed: {}", e); + e + }) } -fn index(_req: HttpRequest) -> Result { +fn index() -> HttpResponse { let html = r#" Upload Test @@ -90,26 +76,23 @@ fn index(_req: HttpRequest) -> Result { "#; - Ok(HttpResponse::Ok().body(html)) + HttpResponse::Ok().body(html) } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); env_logger::init(); - let sys = actix::System::new("multipart-example"); - server::new(|| { - App::with_state(AppState { - counter: Cell::new(0), - }).middleware(middleware::Logger::default()) - .resource("/", |r| { - r.method(http::Method::GET).with(index); - r.method(http::Method::POST).with(upload); - }) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - - println!("Starting http server: 127.0.0.1:8080"); - let _ = sys.run(); + HttpServer::new(|| { + App::new() + .data(Cell::new(0usize)) + .wrap(middleware::Logger::default()) + .service( + web::resource("/") + .route(web::get().to(index)) + .route(web::post().to_async(upload)), + ) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/protobuf/src/main.rs b/protobuf/src/main.rs index aaae1f18..83220301 100644 --- a/protobuf/src/main.rs +++ b/protobuf/src/main.rs @@ -28,10 +28,10 @@ pub struct MyObj { /// This handler uses `ProtoBufMessage` for loading protobuf object. fn index(req: &HttpRequest) -> Box> { protobuf::ProtoBufMessage::new(req) - .from_err() // convert all errors into `Error` + .from_err() // convert all errors into `Error` .and_then(|val: MyObj| { println!("model: {:?}", val); - Ok(HttpResponse::Ok().protobuf(val)?) // <- send response + Ok(HttpResponse::Ok().protobuf(val)?) // <- send response }) .responder() } @@ -45,10 +45,11 @@ fn main() { App::new() .middleware(middleware::Logger::default()) .resource("/", |r| r.method(http::Method::POST).f(index)) - }).bind("127.0.0.1:8080") - .unwrap() - .shutdown_timeout(1) - .start(); + }) + .bind("127.0.0.1:8080") + .unwrap() + .shutdown_timeout(1) + .start(); println!("Started http server: 127.0.0.1:8080"); let _ = sys.run(); diff --git a/r2d2/Cargo.toml b/r2d2/Cargo.toml index 884d84c4..0f6d7b57 100644 --- a/r2d2/Cargo.toml +++ b/r2d2/Cargo.toml @@ -2,20 +2,17 @@ name = "r2d2-example" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +edition = "2018" +workspace = ".." [dependencies] -env_logger = "0.5" - -actix = "0.7" -actix-web = "0.7" +actix-rt = "0.2" +actix-web = "1.0.0-alpha.1" futures = "0.1" -uuid = { version = "0.5", features = ["serde", "v4"] } -serde = "1.0" -serde_json = "1.0" -serde_derive = "1.0" +env_logger = "0.6" +uuid = { version = "0.7", features = ["v4"] } -r2d2 = "*" -r2d2_sqlite = "*" -rusqlite = "*" +r2d2 = "0.8" +r2d2_sqlite = "0.8" +rusqlite = "0.16" diff --git a/r2d2/src/db.rs b/r2d2/src/db.rs deleted file mode 100644 index 5df9b3ff..00000000 --- a/r2d2/src/db.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! Db executor actor -use actix::prelude::*; -use actix_web::*; -use r2d2::Pool; -use r2d2_sqlite::SqliteConnectionManager; -use std::io; -use uuid; - -/// This is db executor actor. We are going to run 3 of them in parallel. -pub struct DbExecutor(pub Pool); - -/// This is only message that this actor can handle, but it is easy to extend -/// number of messages. -pub struct CreateUser { - pub name: String, -} - -impl Message for CreateUser { - type Result = Result; -} - -impl Actor for DbExecutor { - type Context = SyncContext; -} - -impl Handler for DbExecutor { - type Result = Result; - - fn handle(&mut self, msg: CreateUser, _: &mut Self::Context) -> Self::Result { - let conn = self.0.get().unwrap(); - - let uuid = format!("{}", uuid::Uuid::new_v4()); - conn.execute( - "INSERT INTO users (id, name) VALUES ($1, $2)", - &[&uuid, &msg.name], - ).unwrap(); - - Ok(conn - .query_row("SELECT name FROM users WHERE id=$1", &[&uuid], |row| { - row.get(0) - }) - .map_err(|_| io::Error::new(io::ErrorKind::Other, "db error"))?) - } -} diff --git a/r2d2/src/main.rs b/r2d2/src/main.rs index e3cdb421..c323784c 100644 --- a/r2d2/src/main.rs +++ b/r2d2/src/main.rs @@ -1,68 +1,56 @@ //! Actix web r2d2 example -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate futures; -extern crate r2d2; -extern crate r2d2_sqlite; -extern crate rusqlite; -extern crate serde; -extern crate serde_json; -extern crate uuid; +use std::io; -use actix::prelude::*; -use actix_web::{ - http, middleware, server, App, AsyncResponder, Error, HttpRequest, HttpResponse, -}; -use futures::future::Future; +use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer}; +use futures::Future; +use r2d2::Pool; use r2d2_sqlite::SqliteConnectionManager; +use uuid; -mod db; -use db::{CreateUser, DbExecutor}; +/// Async request handler. Ddb pool is stored in application state. +fn index( + path: web::Path, + db: web::Data>, +) -> impl Future { + // execute sync code in threadpool + web::block(move || { + let conn = db.get().unwrap(); -/// State with DbExecutor address -struct State { - db: Addr, + let uuid = format!("{}", uuid::Uuid::new_v4()); + conn.execute( + "INSERT INTO users (id, name) VALUES ($1, $2)", + &[&uuid, &path.into_inner()], + ) + .unwrap(); + + conn.query_row("SELECT name FROM users WHERE id=$1", &[&uuid], |row| { + row.get::<_, String>(0) + }) + }) + .then(|res| match res { + Ok(user) => Ok(HttpResponse::Ok().json(user)), + Err(_) => Ok(HttpResponse::InternalServerError().into()), + }) } -/// Async request handler -fn index(req: &HttpRequest) -> Box> { - let name = &req.match_info()["name"]; - - req.state() - .db - .send(CreateUser { - name: name.to_owned(), - }) - .from_err() - .and_then(|res| match res { - Ok(user) => Ok(HttpResponse::Ok().json(user)), - Err(_) => Ok(HttpResponse::InternalServerError().into()), - }) - .responder() -} - -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=debug"); +fn main() -> io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=debug"); env_logger::init(); - let sys = actix::System::new("r2d2-example"); + let sys = actix_rt::System::new("r2d2-example"); // r2d2 pool let manager = SqliteConnectionManager::file("test.db"); let pool = r2d2::Pool::new(manager).unwrap(); - // Start db executor actors - let addr = SyncArbiter::start(3, move || DbExecutor(pool.clone())); + // start http server + HttpServer::new(move || { + App::new() + .data(pool.clone()) // <- store db pool in app state + .wrap(middleware::Logger::default()) + .route("/{name}", web::get().to_async(index)) + }) + .bind("127.0.0.1:8080")? + .start(); - // Start http server - server::new(move || { - App::with_state(State{db: addr.clone()}) - // enable logger - .middleware(middleware::Logger::default()) - .resource("/{name}", |r| r.method(http::Method::GET).a(index)) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - - let _ = sys.run(); + sys.run() } diff --git a/redis-session/Cargo.toml b/redis-session/Cargo.toml index 8b6152a6..5198d902 100644 --- a/redis-session/Cargo.toml +++ b/redis-session/Cargo.toml @@ -2,10 +2,12 @@ name = "redis-session" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] -env_logger = "0.5" -actix = "0.7" -actix-web = "0.7.3" -actix-redis = { version = "0.5.1", features = ["web"] } +actix = "0.8.0-alpha.2" +actix-web = "1.0.0-alpha.1" +actix-session = "0.1.0-alpha.1" +actix-redis = { git = "https://github.com/actix/actix-redis.git", features = ["web"] } +env_logger = "0.6" diff --git a/redis-session/src/main.rs b/redis-session/src/main.rs index 3b68ab56..0c49c6bf 100644 --- a/redis-session/src/main.rs +++ b/redis-session/src/main.rs @@ -1,48 +1,38 @@ //! Example of redis based session //! //! [User guide](https://actix.rs/book/actix-web/sec-9-middlewares.html#user-sessions) -extern crate actix; -extern crate actix_redis; -extern crate actix_web; -extern crate env_logger; - -use actix_redis::RedisSessionBackend; -use actix_web::middleware::session::{RequestSession, SessionStorage}; -use actix_web::{middleware, server, App, HttpRequest, HttpResponse, Result}; +use actix_redis::RedisSession; +use actix_session::Session; +use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer, Result}; /// simple handler -fn index(req: &HttpRequest) -> Result { +fn index(req: HttpRequest, session: Session) -> Result { println!("{:?}", req); // session - if let Some(count) = req.session().get::("counter")? { + if let Some(count) = session.get::("counter")? { println!("SESSION value: {}", count); - req.session().set("counter", count + 1)?; + session.set("counter", count + 1)?; } else { - req.session().set("counter", 1)?; + session.set("counter", 1)?; } Ok("Welcome!".into()) } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info,actix_redis=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info,actix_redis=info"); env_logger::init(); - let sys = actix::System::new("basic-example"); - server::new(|| { + HttpServer::new(|| { App::new() // enable logger - .middleware(middleware::Logger::default()) + .wrap(middleware::Logger::default()) // redis session middleware - .middleware(SessionStorage::new( - RedisSessionBackend::new("127.0.0.1:6379", &[0; 32]) - )) + .wrap(RedisSession::new("127.0.0.1:6379", &[0; 32])) // register simple route, handle all methods - .resource("/", |r| r.f(index)) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - - let _ = sys.run(); + .service(web::resource("/").to(index)) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/rustfmt.toml b/rustfmt.toml index 4fff285e..94bd11d5 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,5 +1,2 @@ max_width = 89 reorder_imports = true -#wrap_comments = true -fn_args_density = "Compressed" -#use_small_heuristics = false diff --git a/rustls/src/main.rs b/rustls/src/main.rs index 58220215..33bcb538 100644 --- a/rustls/src/main.rs +++ b/rustls/src/main.rs @@ -49,16 +49,19 @@ fn main() { // register simple handler, handle all methods .resource("/index.html", |r| r.f(index)) // with path parameters - .resource("/", |r| r.method(http::Method::GET).f(|_| { - HttpResponse::Found() - .header("LOCATION", "/index.html") - .finish() - })) + .resource("/", |r| { + r.method(http::Method::GET).f(|_| { + HttpResponse::Found() + .header("LOCATION", "/index.html") + .finish() + }) + }) .handler("/static", StaticFiles::new("static").unwrap()) - }).bind_with("127.0.0.1:8443", move || acceptor.clone()) + }) + .bind_with("127.0.0.1:8443", move || acceptor.clone()) .unwrap() .start(); println!("Started http server: 127.0.0.1:8443"); let _ = sys.run(); -} \ No newline at end of file +} diff --git a/simple-auth-server/Cargo.toml b/simple-auth-server/Cargo.toml index 25ba9407..0c6a42be 100644 --- a/simple-auth-server/Cargo.toml +++ b/simple-auth-server/Cargo.toml @@ -2,18 +2,22 @@ name = "simple-auth-server" version = "0.1.0" authors = ["mygnu "] +edition = "2018" +workspace = ".." [dependencies] -actix = "0.7.7" -actix-web = "0.7.14" +actix = { version = "0.8.0-alpha.2", features = ["http"] } +actix-web = "1.0.0-alpha.1" +actix-files = "0.1.0-alpha.1" + bcrypt = "0.2.1" chrono = { version = "0.4.6", features = ["serde"] } diesel = { version = "1.3.3", features = ["postgres", "uuid", "r2d2", "chrono"] } dotenv = "0.13.0" +derive_more = "0.14" env_logger = "0.6.0" -failure = "0.1.3" jsonwebtoken = "5.0" -futures = "0.1" +futures = "0.1.25" r2d2 = "0.8.3" serde_derive="1.0.80" serde_json="1.0" diff --git a/simple-auth-server/src/app.rs b/simple-auth-server/src/app.rs deleted file mode 100644 index f56be1a2..00000000 --- a/simple-auth-server/src/app.rs +++ /dev/null @@ -1,54 +0,0 @@ -use actix::prelude::*; -use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService}; -use actix_web::{fs, http::Method, middleware::Logger, App}; -use auth_routes::{get_me, login, logout}; -use chrono::Duration; -use invitation_routes::register_email; -use models::DbExecutor; -use register_routes::register_user; - -pub struct AppState { - pub db: Addr, -} - -/// creates and returns the app after mounting all routes/resources -pub fn create_app(db: Addr) -> App { - // secret is a random minimum 32 bytes long base 64 string - let secret: String = std::env::var("SECRET_KEY").unwrap_or_else(|_| "0123".repeat(8)); - let domain: String = std::env::var("DOMAIN").unwrap_or_else(|_| "localhost".to_string()); - - App::with_state(AppState { db }) - .middleware(Logger::default()) - .middleware(IdentityService::new( - CookieIdentityPolicy::new(secret.as_bytes()) - .name("auth") - .path("/") - .domain(domain.as_str()) - .max_age(Duration::days(1)) - .secure(false), // this can only be true if you have https - )) - // everything under '/api/' route - .scope("/api", |api| { - // routes for authentication - api.resource("/auth", |r| { - r.method(Method::POST).with(login); - r.method(Method::DELETE).with(logout); - r.method(Method::GET).with(get_me); - }) - // routes to invitation - .resource("/invitation", |r| { - r.method(Method::POST).with(register_email); - }) - // routes to register as a user after the - .resource("/register/{invitation_id}", |r| { - r.method(Method::POST).with(register_user); - }) - }) - // serve static files - .handler( - "/", - fs::StaticFiles::new("./static/") - .unwrap() - .index_file("index.html"), - ) -} diff --git a/simple-auth-server/src/auth_handler.rs b/simple-auth-server/src/auth_handler.rs index cedfa90e..13eb1476 100644 --- a/simple-auth-server/src/auth_handler.rs +++ b/simple-auth-server/src/auth_handler.rs @@ -1,10 +1,12 @@ use actix::{Handler, Message}; -use diesel::prelude::*; -use errors::ServiceError; -use models::{DbExecutor, User, SlimUser}; +use actix_web::{dev::ServiceFromRequest, Error}; +use actix_web::{middleware::identity::Identity, FromRequest, HttpRequest}; use bcrypt::verify; -use actix_web::{FromRequest, HttpRequest, middleware::identity::RequestIdentity}; -use utils::decode_token; +use diesel::prelude::*; + +use crate::errors::ServiceError; +use crate::models::{DbExecutor, SlimUser, User}; +use crate::utils::decode_token; #[derive(Debug, Deserialize)] pub struct AuthData { @@ -19,22 +21,24 @@ impl Message for AuthData { impl Handler for DbExecutor { type Result = Result; fn handle(&mut self, msg: AuthData, _: &mut Self::Context) -> Self::Result { - use schema::users::dsl::{users, email}; + use crate::schema::users::dsl::{email, users}; let conn: &PgConnection = &self.0.get().unwrap(); - let mut items = users - .filter(email.eq(&msg.email)) - .load::(conn)?; + let mut items = users.filter(email.eq(&msg.email)).load::(conn)?; if let Some(user) = items.pop() { match verify(&msg.password, &user.password) { - Ok(matching) => if matching { + Ok(matching) => { + if matching { return Ok(user.into()); - }, + } + } Err(_) => (), } } - Err(ServiceError::BadRequest("Username and Password don't match".into())) + Err(ServiceError::BadRequest( + "Username and Password don't match".into(), + )) } } @@ -42,14 +46,15 @@ impl Handler for DbExecutor { // simple aliasing makes the intentions clear and its more readable pub type LoggedUser = SlimUser; -impl FromRequest for LoggedUser { - type Config = (); - type Result = Result; - fn from_request(req: &HttpRequest, _: &Self::Config) -> Self::Result { - if let Some(identity) = req.identity() { +impl

FromRequest

for LoggedUser { + type Error = Error; + type Future = Result; + + fn from_request(req: &mut ServiceFromRequest

) -> Self::Future { + if let Some(identity) = Identity::from_request(req)?.identity() { let user: SlimUser = decode_token(&identity)?; return Ok(user as LoggedUser); } - Err(ServiceError::Unauthorized) + Err(ServiceError::Unauthorized.into()) } } diff --git a/simple-auth-server/src/auth_routes.rs b/simple-auth-server/src/auth_routes.rs index 48ee15c0..269c3a67 100644 --- a/simple-auth-server/src/auth_routes.rs +++ b/simple-auth-server/src/auth_routes.rs @@ -1,30 +1,32 @@ -use actix_web::{AsyncResponder, FutureResponse, HttpResponse, HttpRequest, ResponseError, Json}; -use actix_web::middleware::identity::RequestIdentity; -use futures::future::Future; -use utils::create_token; +use actix::Addr; +use actix_web::middleware::identity::Identity; +use actix_web::{web, Error, HttpRequest, HttpResponse, Responder, ResponseError}; +use futures::Future; -use app::AppState; -use auth_handler::{AuthData, LoggedUser}; +use crate::auth_handler::{AuthData, LoggedUser}; +use crate::models::DbExecutor; +use crate::utils::create_token; -pub fn login((auth_data, req): (Json, HttpRequest)) - -> FutureResponse { - req.state() - .db - .send(auth_data.into_inner()) +pub fn login( + auth_data: web::Json, + id: Identity, + db: web::Data>, +) -> impl Future { + db.send(auth_data.into_inner()) .from_err() .and_then(move |res| match res { Ok(user) => { let token = create_token(&user)?; - req.remember(token); + id.remember(token); Ok(HttpResponse::Ok().into()) } Err(err) => Ok(err.error_response()), - }).responder() + }) } -pub fn logout(req: HttpRequest) -> HttpResponse { - req.forget(); - HttpResponse::Ok().into() +pub fn logout(id: Identity) -> impl Responder { + id.forget(); + HttpResponse::Ok() } pub fn get_me(logged_user: LoggedUser) -> HttpResponse { diff --git a/simple-auth-server/src/email_service.rs b/simple-auth-server/src/email_service.rs index f1db1172..290d37ab 100644 --- a/simple-auth-server/src/email_service.rs +++ b/simple-auth-server/src/email_service.rs @@ -1,4 +1,4 @@ -use models::Invitation; +use crate::models::Invitation; use sparkpost::transmission::{ EmailAddress, Message, Options, Recipient, Transmission, TransmissionResponse, }; @@ -9,8 +9,8 @@ fn get_api_key() -> String { pub fn send_invitation(invitation: &Invitation) { let tm = Transmission::new_eu(get_api_key()); - let sending_email = - std::env::var("SENDING_EMAIL_ADDRESS").expect("SENDING_EMAIL_ADDRESS must be set"); + let sending_email = std::env::var("SENDING_EMAIL_ADDRESS") + .expect("SENDING_EMAIL_ADDRESS must be set"); // new email message with sender name and email let mut email = Message::new(EmailAddress::new(sending_email, "Let's Organise")); @@ -39,7 +39,6 @@ pub fn send_invitation(invitation: &Invitation) { .to_string() ); - // complete the email message with details email .add_recipient(recipient) @@ -51,16 +50,14 @@ pub fn send_invitation(invitation: &Invitation) { // Note that we only print out the error response from email api match result { - Ok(res) => { - match res { - TransmissionResponse::ApiResponse(api_res) => { - println!("API Response: \n {:#?}", api_res); - } - TransmissionResponse::ApiError(errors) => { - println!("Response Errors: \n {:#?}", &errors); - } + Ok(res) => match res { + TransmissionResponse::ApiResponse(api_res) => { + println!("API Response: \n {:#?}", api_res); } - } + TransmissionResponse::ApiError(errors) => { + println!("Response Errors: \n {:#?}", &errors); + } + }, Err(error) => { println!("error \n {:#?}", error); } diff --git a/simple-auth-server/src/errors.rs b/simple-auth-server/src/errors.rs index b65aa4f7..bac56094 100644 --- a/simple-auth-server/src/errors.rs +++ b/simple-auth-server/src/errors.rs @@ -1,18 +1,18 @@ use actix_web::{error::ResponseError, HttpResponse}; -use std::convert::From; +use derive_more::Display; use diesel::result::{DatabaseErrorKind, Error}; +use std::convert::From; use uuid::ParseError; - -#[derive(Fail, Debug)] +#[derive(Debug, Display)] pub enum ServiceError { - #[fail(display = "Internal Server Error")] + #[display(fmt = "Internal Server Error")] InternalServerError, - #[fail(display = "BadRequest: {}", _0)] + #[display(fmt = "BadRequest: {}", _0)] BadRequest(String), - #[fail(display = "Unauthorized")] + #[display(fmt = "Unauthorized")] Unauthorized, } @@ -20,9 +20,14 @@ pub enum ServiceError { impl ResponseError for ServiceError { fn error_response(&self) -> HttpResponse { match *self { - ServiceError::InternalServerError => HttpResponse::InternalServerError().json("Internal Server Error, Please try later"), - ServiceError::BadRequest(ref message) => HttpResponse::BadRequest().json(message), - ServiceError::Unauthorized => HttpResponse::Unauthorized().json("Unauthorized") + ServiceError::InternalServerError => HttpResponse::InternalServerError() + .json("Internal Server Error, Please try later"), + ServiceError::BadRequest(ref message) => { + HttpResponse::BadRequest().json(message) + } + ServiceError::Unauthorized => { + HttpResponse::Unauthorized().json("Unauthorized") + } } } } @@ -42,12 +47,13 @@ impl From for ServiceError { match error { Error::DatabaseError(kind, info) => { if let DatabaseErrorKind::UniqueViolation = kind { - let message = info.details().unwrap_or_else(|| info.message()).to_string(); + let message = + info.details().unwrap_or_else(|| info.message()).to_string(); return ServiceError::BadRequest(message); } ServiceError::InternalServerError } - _ => ServiceError::InternalServerError + _ => ServiceError::InternalServerError, } } } diff --git a/simple-auth-server/src/invitation_handler.rs b/simple-auth-server/src/invitation_handler.rs index 425e4c0a..b0a8ceb7 100644 --- a/simple-auth-server/src/invitation_handler.rs +++ b/simple-auth-server/src/invitation_handler.rs @@ -1,10 +1,11 @@ use actix::{Handler, Message}; use chrono::{Duration, Local}; use diesel::{self, prelude::*}; -use errors::ServiceError; -use models::{DbExecutor, Invitation}; use uuid::Uuid; +use crate::errors::ServiceError; +use crate::models::{DbExecutor, Invitation}; + #[derive(Deserialize)] pub struct CreateInvitation { pub email: String, @@ -18,7 +19,7 @@ impl Handler for DbExecutor { type Result = Result; fn handle(&mut self, msg: CreateInvitation, _: &mut Self::Context) -> Self::Result { - use schema::invitations::dsl::*; + use crate::schema::invitations::dsl::*; let conn: &PgConnection = &self.0.get().unwrap(); // creating a new Invitation object with expired at time that is 24 hours from now @@ -35,5 +36,3 @@ impl Handler for DbExecutor { Ok(inserted_invitation) } } - - diff --git a/simple-auth-server/src/invitation_routes.rs b/simple-auth-server/src/invitation_routes.rs index 9232c6d0..ea76bca2 100644 --- a/simple-auth-server/src/invitation_routes.rs +++ b/simple-auth-server/src/invitation_routes.rs @@ -1,16 +1,16 @@ -use actix_web::{AsyncResponder, FutureResponse, HttpResponse, Json, ResponseError, State}; +use actix::Addr; +use actix_web::{web, Error, HttpResponse, ResponseError}; use futures::future::Future; -use app::AppState; -use email_service::send_invitation; -use invitation_handler::CreateInvitation; +use crate::email_service::send_invitation; +use crate::invitation_handler::CreateInvitation; +use crate::models::DbExecutor; pub fn register_email( - (signup_invitation, state): (Json, State), -) -> FutureResponse { - state - .db - .send(signup_invitation.into_inner()) + signup_invitation: web::Json, + db: web::Data>, +) -> impl Future { + db.send(signup_invitation.into_inner()) .from_err() .and_then(|db_response| match db_response { Ok(invitation) => { @@ -18,5 +18,5 @@ pub fn register_email( Ok(HttpResponse::Ok().into()) } Err(err) => Ok(err.error_response()), - }).responder() + }) } diff --git a/simple-auth-server/src/main.rs b/simple-auth-server/src/main.rs index f5d096b2..90024095 100644 --- a/simple-auth-server/src/main.rs +++ b/simple-auth-server/src/main.rs @@ -1,53 +1,44 @@ -// to avoid the warning from diesel macros -#![allow(proc_macro_derive_resolution_fallback)] +#![allow(unused_imports)] -extern crate bcrypt; -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate serde; -extern crate chrono; -extern crate dotenv; -extern crate futures; -extern crate r2d2; -extern crate uuid; -extern crate jsonwebtoken as jwt; -extern crate sparkpost; #[macro_use] extern crate diesel; #[macro_use] extern crate serde_derive; -#[macro_use] -extern crate failure; -mod app; -mod models; -mod schema; -mod errors; -mod auth_handler; -mod auth_routes; -mod invitation_handler; -mod invitation_routes; -mod register_handler; -mod register_routes; -mod utils; -mod email_service; - -use models::DbExecutor; use actix::prelude::*; -use actix_web::server; +use actix_files as fs; +use actix_web::middleware::{ + identity::{CookieIdentityPolicy, IdentityService}, + Logger, +}; +use actix_web::{web, App, HttpServer}; +use chrono::Duration; use diesel::{r2d2::ConnectionManager, PgConnection}; use dotenv::dotenv; -use std::env; +mod auth_handler; +mod auth_routes; +mod email_service; +mod errors; +mod invitation_handler; +mod invitation_routes; +mod models; +mod register_handler; +mod register_routes; +mod schema; +mod utils; -fn main() { +use crate::models::DbExecutor; + +fn main() -> std::io::Result<()> { dotenv().ok(); - std::env::set_var("RUST_LOG", "simple-auth-server=debug,actix_web=info"); - std::env::set_var("RUST_BACKTRACE", "1"); + std::env::set_var( + "RUST_LOG", + "simple-auth-server=debug,actix_web=info,actix_server=info", + ); env_logger::init(); - let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); - let sys = actix::System::new("Actix_Tutorial"); + + let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); // create db connection pool let manager = ConnectionManager::::new(database_url); @@ -55,12 +46,52 @@ fn main() { .build(manager) .expect("Failed to create pool."); - let address: Addr = SyncArbiter::start(4, move || DbExecutor(pool.clone())); + let address: Addr = + SyncArbiter::start(4, move || DbExecutor(pool.clone())); - server::new(move || app::create_app(address.clone())) - .bind("127.0.0.1:3000") - .expect("Can not bind to '127.0.0.1:3000'") - .start(); + HttpServer::new(move || { + // secret is a random minimum 32 bytes long base 64 string + let secret: String = + std::env::var("SECRET_KEY").unwrap_or_else(|_| "0123".repeat(8)); + let domain: String = + std::env::var("DOMAIN").unwrap_or_else(|_| "localhost".to_string()); - sys.run(); + App::new() + .data(address.clone()) + .wrap(Logger::default()) + .wrap(IdentityService::new( + CookieIdentityPolicy::new(secret.as_bytes()) + .name("auth") + .path("/") + .domain(domain.as_str()) + .max_age(Duration::days(1)) + .secure(false), // this can only be true if you have https + )) + // everything under '/api/' route + .service( + web::scope("/api") + // routes for authentication + .service( + web::resource("/auth") + .route(web::post().to_async(auth_routes::login)) + .route(web::delete().to(auth_routes::logout)) + .route(web::get().to_async(auth_routes::get_me)), + ) + // routes to invitation + .service( + web::resource("/invitation").route( + web::post().to_async(invitation_routes::register_email), + ), + ) + // routes to register as a user after the + .service( + web::resource("/register/{invitation_id}") + .route(web::post().to_async(register_routes::register_user)), + ), + ) + // serve static files + .service(fs::Files::new("/", "./static/").index_file("index.html")) + }) + .bind("127.0.0.1:3000")? + .run() } diff --git a/simple-auth-server/src/models.rs b/simple-auth-server/src/models.rs index fcafb9c5..76dcfac7 100644 --- a/simple-auth-server/src/models.rs +++ b/simple-auth-server/src/models.rs @@ -1,11 +1,11 @@ use actix::{Actor, SyncContext}; +use chrono::{Local, NaiveDateTime}; use diesel::pg::PgConnection; use diesel::r2d2::{ConnectionManager, Pool}; -use chrono::{NaiveDateTime, Local}; -use uuid::Uuid; use std::convert::From; +use uuid::Uuid; -use schema::{users, invitations}; +use crate::schema::{invitations, users}; /// This is db executor actor. can be run in parallel pub struct DbExecutor(pub Pool>); @@ -51,8 +51,6 @@ pub struct SlimUser { impl From for SlimUser { fn from(user: User) -> Self { - SlimUser { - email: user.email - } + SlimUser { email: user.email } } } diff --git a/simple-auth-server/src/register_handler.rs b/simple-auth-server/src/register_handler.rs index 7cf98fd3..04fe164e 100644 --- a/simple-auth-server/src/register_handler.rs +++ b/simple-auth-server/src/register_handler.rs @@ -1,10 +1,11 @@ use actix::{Handler, Message}; use chrono::Local; use diesel::prelude::*; -use errors::ServiceError; -use models::{DbExecutor, Invitation, User, SlimUser}; use uuid::Uuid; -use utils::hash_password; + +use crate::errors::ServiceError; +use crate::models::{DbExecutor, Invitation, SlimUser, User}; +use crate::utils::hash_password; // UserData is used to extract data from a post request by the client #[derive(Debug, Deserialize)] @@ -23,19 +24,19 @@ impl Message for RegisterUser { type Result = Result; } - impl Handler for DbExecutor { type Result = Result; fn handle(&mut self, msg: RegisterUser, _: &mut Self::Context) -> Self::Result { - use schema::invitations::dsl::{invitations, id}; - use schema::users::dsl::users; + use crate::schema::invitations::dsl::{id, invitations}; + use crate::schema::users::dsl::users; let conn: &PgConnection = &self.0.get().unwrap(); // try parsing the string provided by the user as url parameter // return early with error that will be converted to ServiceError let invitation_id = Uuid::parse_str(&msg.invitation_id)?; - invitations.filter(id.eq(invitation_id)) + invitations + .filter(id.eq(invitation_id)) .load::(conn) .map_err(|_db_error| ServiceError::BadRequest("Invalid Invitation".into())) .and_then(|mut result| { @@ -45,9 +46,8 @@ impl Handler for DbExecutor { // try hashing the password, else return the error that will be converted to ServiceError let password: String = hash_password(&msg.password)?; let user = User::with_details(invitation.email, password); - let inserted_user: User = diesel::insert_into(users) - .values(&user) - .get_result(conn)?; + let inserted_user: User = + diesel::insert_into(users).values(&user).get_result(conn)?; return Ok(inserted_user.into()); } @@ -56,5 +56,3 @@ impl Handler for DbExecutor { }) } } - - diff --git a/simple-auth-server/src/register_routes.rs b/simple-auth-server/src/register_routes.rs index 5924ba50..19b3866f 100644 --- a/simple-auth-server/src/register_routes.rs +++ b/simple-auth-server/src/register_routes.rs @@ -1,22 +1,25 @@ -use actix_web::{AsyncResponder, FutureResponse, HttpResponse, ResponseError, State, Json, Path}; -use futures::future::Future; +use actix::Addr; +use actix_web::{web, Error, HttpResponse, ResponseError}; +use futures::Future; -use app::AppState; -use register_handler::{RegisterUser, UserData}; +use crate::models::DbExecutor; +use crate::register_handler::{RegisterUser, UserData}; - -pub fn register_user((invitation_id, user_data, state): (Path, Json, State)) - -> FutureResponse { +pub fn register_user( + invitation_id: web::Path, + user_data: web::Json, + db: web::Data>, +) -> impl Future { let msg = RegisterUser { // into_inner() returns the inner string value from Path invitation_id: invitation_id.into_inner(), password: user_data.password.clone(), }; - state.db.send(msg) + db.send(msg) .from_err() .and_then(|db_response| match db_response { Ok(slim_user) => Ok(HttpResponse::Ok().json(slim_user)), Err(service_error) => Ok(service_error.error_response()), - }).responder() + }) } diff --git a/simple-auth-server/src/schema.rs b/simple-auth-server/src/schema.rs index 646632db..95c37038 100644 --- a/simple-auth-server/src/schema.rs +++ b/simple-auth-server/src/schema.rs @@ -14,7 +14,4 @@ table! { } } -allow_tables_to_appear_in_same_query!( - invitations, - users, -); +allow_tables_to_appear_in_same_query!(invitations, users,); diff --git a/simple-auth-server/src/utils.rs b/simple-auth-server/src/utils.rs index 1f65d3e8..37035277 100644 --- a/simple-auth-server/src/utils.rs +++ b/simple-auth-server/src/utils.rs @@ -1,14 +1,13 @@ use bcrypt::{hash, DEFAULT_COST}; use chrono::{Duration, Local}; -use errors::ServiceError; -use jwt::{decode, encode, Header, Validation}; -use models::SlimUser; -use std::convert::From; -use std::env; +use jsonwebtoken::{decode, encode, Header, Validation}; + +use crate::errors::ServiceError; +use crate::models::SlimUser; pub fn hash_password(plain: &str) -> Result { // get the hashing cost from the env variable or use default - let hashing_cost: u32 = match env::var("HASH_ROUNDS") { + let hashing_cost: u32 = match std::env::var("HASH_ROUNDS") { Ok(cost) => cost.parse().unwrap_or(DEFAULT_COST), _ => DEFAULT_COST, }; @@ -64,5 +63,5 @@ pub fn decode_token(token: &str) -> Result { } fn get_secret() -> String { - env::var("JWT_SECRET").unwrap_or_else(|_| "my secret".into()) + std::env::var("JWT_SECRET").unwrap_or_else(|_| "my secret".into()) } diff --git a/state/Cargo.toml b/state/Cargo.toml index edf0e382..962b35d8 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -2,11 +2,10 @@ name = "state" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] -futures = "0.1" -env_logger = "0.5" - -actix = "0.7" -actix-web = "0.7" +actix-web = "1.0.0-alpha.1" +futures = "0.1.25" +env_logger = "0.6" diff --git a/state/src/main.rs b/state/src/main.rs index be1bd465..3ebb38a9 100644 --- a/state/src/main.rs +++ b/state/src/main.rs @@ -1,55 +1,44 @@ #![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] -//! There are two level of statefulness in actix-web. Application has state -//! that is shared across all handlers within same Application. -//! And individual handler can have state. +//! Application may have multiple data objects that are shared across +//! all handlers within same Application. Data could be added +//! with `App::data()` method, multiple different data objects could be added. //! //! > **Note**: http server accepts an application factory rather than an //! application > instance. Http server constructs an application instance for -//! each thread, > thus application state -//! > must be constructed multiple times. If you want to share state between +//! each thread, > thus application data +//! > must be constructed multiple times. If you want to share data between //! different > threads, a shared object should be used, e.g. `Arc`. //! //! Check [user guide](https://actix.rs/book/actix-web/sec-2-application.html) for more info. -extern crate actix; -extern crate actix_web; -extern crate env_logger; +use std::io; +use std::sync::{Arc, Mutex}; -use std::sync::Arc; -use std::sync::Mutex; - -use actix_web::{middleware, server, App, HttpRequest, HttpResponse}; - -/// Application state -struct AppState { - counter: Arc>, -} +use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer}; /// simple handle -fn index(req: &HttpRequest) -> HttpResponse { +fn index(state: web::Data>>, req: HttpRequest) -> HttpResponse { println!("{:?}", req); - *(req.state().counter.lock().unwrap()) += 1; + *(state.lock().unwrap()) += 1; - HttpResponse::Ok().body(format!("Num of requests: {}", req.state().counter.lock().unwrap())) + HttpResponse::Ok().body(format!("Num of requests: {}", state.lock().unwrap())) } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("ws-example"); let counter = Arc::new(Mutex::new(0)); - //move is necessary to give closure below ownership of counter - server::new(move || { - App::with_state(AppState{counter: counter.clone()}) // <- create app with shared state - // enable logger - .middleware(middleware::Logger::default()) - // register simple handler, handle all methods - .resource("/", |r| r.f(index)) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + //move is necessary to give closure below ownership of counter + HttpServer::new(move || { + App::new() + .data(counter.clone()) // <- create app with shared state + // enable logger + .wrap(middleware::Logger::default()) + // register simple handler, handle all methods + .service(web::resource("/").to(index)) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/static_index/Cargo.toml b/static_index/Cargo.toml index 6f427322..6ae309a8 100644 --- a/static_index/Cargo.toml +++ b/static_index/Cargo.toml @@ -2,11 +2,12 @@ name = "static_index" version = "0.1.0" authors = ["Jose Marinez "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] futures = "0.1" env_logger = "0.5" -actix = "0.7" -actix-web = "0.7" +actix-web = "1.0.0-alpha.1" +actix-files = "0.1.0-alpha.1" diff --git a/static_index/src/main.rs b/static_index/src/main.rs index 25c77c03..ced7b689 100644 --- a/static_index/src/main.rs +++ b/static_index/src/main.rs @@ -1,28 +1,19 @@ -extern crate actix; -extern crate actix_web; -extern crate env_logger; +use actix_files as fs; +use actix_web::{middleware, App, HttpServer}; -use actix_web::{fs, middleware, server, App}; - -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); - ::std::env::set_var("RUST_BACKTRACE", "1"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("static_index"); - - server::new(|| { + HttpServer::new(|| { App::new() - // enable logger - .middleware(middleware::Logger::default()) - .handler( - "/", - fs::StaticFiles::new("./static/").unwrap().index_file("index.html") + // enable logger + .wrap(middleware::Logger::default()) + .service( + // static files + fs::Files::new("/", "./static/").index_file("index.html"), ) - }).bind("127.0.0.1:8080") - .expect("Can not start server on given IP/Port") - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/template_askama/Cargo.toml b/template_askama/Cargo.toml index 03b2a3fe..d88fed35 100644 --- a/template_askama/Cargo.toml +++ b/template_askama/Cargo.toml @@ -2,14 +2,14 @@ name = "template-askama" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] -env_logger = "0.5" -askama = { version = "0.8", features = ["with-actix-web"] } - -actix = "0.7" -actix-web = "0.7" +#askama = { version = "0.8", features = ["with-actix-web"] } +actix-web = "1.0.0-alpha.1" +env_logger = "0.6" +askama = "0.8" [build-dependencies] askama = "0.8" diff --git a/template_askama/src/main.rs b/template_askama/src/main.rs index 50829e82..b07879ab 100644 --- a/template_askama/src/main.rs +++ b/template_askama/src/main.rs @@ -1,13 +1,7 @@ -extern crate actix; -extern crate actix_web; -#[macro_use] -extern crate askama; - use std::collections::HashMap; -use actix_web::{http, server, App, HttpResponse, Query, Result}; +use actix_web::{web, App, HttpResponse, HttpServer, Result}; use askama::Template; -use askama::actix_web::TemplateIntoResponse; #[derive(Template)] #[template(path = "user.html")] @@ -20,27 +14,25 @@ struct UserTemplate<'a> { #[template(path = "index.html")] struct Index; -fn index(query: Query>) -> Result { - if let Some(name) = query.get("name") { +fn index(query: web::Query>) -> Result { + let s = if let Some(name) = query.get("name") { UserTemplate { name: name, text: "Welcome!", - }.into_response() - } else { - Index.into_response() - } -} - -fn main() { - let sys = actix::System::new("template-askama"); - - // start http server - server::new(move || { - App::new().resource("/", |r| r.method(http::Method::GET).with(index)) - }).bind("127.0.0.1:8080") + } + .render() .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + } else { + Index.render().unwrap() + }; + Ok(HttpResponse::Ok().content_type("text/html").body(s)) +} + +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() } diff --git a/template_tera/Cargo.toml b/template_tera/Cargo.toml index 9e55bf4a..81ecb160 100644 --- a/template_tera/Cargo.toml +++ b/template_tera/Cargo.toml @@ -2,10 +2,10 @@ name = "template-tera" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [dependencies] -env_logger = "0.5" -tera = "*" -actix = "0.7" -actix-web = "0.7" +env_logger = "0.6" +tera = "0.11" +actix-web = "1.0.0-alpha.1" diff --git a/template_tera/src/main.rs b/template_tera/src/main.rs index e7e2c780..a5942140 100644 --- a/template_tera/src/main.rs +++ b/template_tera/src/main.rs @@ -1,57 +1,42 @@ -extern crate actix; -extern crate actix_web; -extern crate env_logger; #[macro_use] extern crate tera; use std::collections::HashMap; -use actix_web::{ - error, http, middleware, server, App, Error, HttpResponse, Query, State, -}; - -struct AppState { - template: tera::Tera, // <- store tera template in application state -} +use actix_web::{error, middleware, web, App, Error, HttpResponse, HttpServer}; +// store tera template in application state fn index( - (state, query): (State, Query>), + tmpl: web::Data, + query: web::Query>, ) -> Result { let s = if let Some(name) = query.get("name") { - // <- submitted form + // submitted form let mut ctx = tera::Context::new(); - ctx.add("name", &name.to_owned()); - ctx.add("text", &"Welcome!".to_owned()); - state - .template - .render("user.html", &ctx) + ctx.insert("name", &name.to_owned()); + ctx.insert("text", &"Welcome!".to_owned()); + tmpl.render("user.html", &ctx) .map_err(|_| error::ErrorInternalServerError("Template error"))? } else { - state - .template - .render("index.html", &tera::Context::new()) + tmpl.render("index.html", &tera::Context::new()) .map_err(|_| error::ErrorInternalServerError("Template error"))? }; Ok(HttpResponse::Ok().content_type("text/html").body(s)) } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("tera-example"); - server::new(|| { + HttpServer::new(|| { let tera = compile_templates!(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")); - App::with_state(AppState{template: tera}) - // enable logger - .middleware(middleware::Logger::default()) - .resource("/", |r| r.method(http::Method::GET).with(index)) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + App::new() + .data(tera) + .wrap(middleware::Logger::default()) // enable logger + .service(web::resource("/").route(web::get().to(index))) + }) + .bind("127.0.0.1:8080")? + .run() } diff --git a/template_yarte/Cargo.toml b/template_yarte/Cargo.toml new file mode 100644 index 00000000..328052ee --- /dev/null +++ b/template_yarte/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "example" +version = "0.0.1" +authors = ["Rust-iendo Barcelona "] +publish = false +edition = "2018" +workspace = ".." + +[dependencies] +actix-web = "1.0.0-alpha.1" +env_logger = "0.6" +yarte = "0.1" + +[build-dependencies] +yarte = "0.1" diff --git a/template_yarte/README.md b/template_yarte/README.md new file mode 100644 index 00000000..df534a7c --- /dev/null +++ b/template_yarte/README.md @@ -0,0 +1,11 @@ +# yarte + +Example of composition with partials and `with-actix-web` feature + +See the generated code on stdout when run at debug +```bash +cargo run +``` +> open `localhost:8080` + +More at [mdbook](https://yarte.netlify.com/) and [repository](https://gitlab.com/r-iendo/yarte) diff --git a/template_yarte/build.rs b/template_yarte/build.rs new file mode 100644 index 00000000..da256d18 --- /dev/null +++ b/template_yarte/build.rs @@ -0,0 +1,5 @@ +use yarte::recompile; + +fn main() { + recompile::when_changed(); +} diff --git a/template_yarte/src/lib.rs b/template_yarte/src/lib.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/template_yarte/src/lib.rs @@ -0,0 +1 @@ + diff --git a/template_yarte/src/main.rs b/template_yarte/src/main.rs new file mode 100644 index 00000000..cd4434b7 --- /dev/null +++ b/template_yarte/src/main.rs @@ -0,0 +1,43 @@ +#[macro_use] +extern crate actix_web; + +use std::collections::HashMap; + +use actix_web::{ + error::ErrorInternalServerError, middleware, web::Query, App, HttpResponse, + HttpServer, Result, +}; +use yarte::Template; + +#[derive(Template)] +#[template(path = "index.hbs")] +struct IndexTemplate { + query: Query>, +} + +#[get("/")] +pub fn index(query: Query>) -> Result { + IndexTemplate { query } + .call() + .map(|s| { + HttpResponse::Ok() + .content_type(IndexTemplate::mime()) + .body(s) + }) + .map_err(|_| ErrorInternalServerError("Template parsing error")) +} + +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); + env_logger::init(); + + // start http server + HttpServer::new(|| { + App::new() + // enable logger + .wrap(middleware::Logger::default()) + .service(index) + }) + .bind("127.0.0.1:8080")? + .run() +} diff --git a/template_yarte/templates/deep/more/deep/welcome.hbs b/template_yarte/templates/deep/more/deep/welcome.hbs new file mode 100644 index 00000000..5cb2a971 --- /dev/null +++ b/template_yarte/templates/deep/more/deep/welcome.hbs @@ -0,0 +1 @@ +<{{ tag }}>Welcome! diff --git a/template_yarte/templates/index.hbs b/template_yarte/templates/index.hbs new file mode 100644 index 00000000..503bb845 --- /dev/null +++ b/template_yarte/templates/index.hbs @@ -0,0 +1,22 @@ + + + + + Actix web + + +{{~#if let Some(name) = query.get("name") ~}} +

Hi, {{ name }}!

+ {{~> alias/welcome tag='p' ~}} +{{~ else ~}} + {{~> alias/welcome tag="h1" ~}} +

+

What is your name?

+
+
+

+
+

+{{~/if~}} + + diff --git a/template_yarte/yarte.toml b/template_yarte/yarte.toml new file mode 100644 index 00000000..bcf89202 --- /dev/null +++ b/template_yarte/yarte.toml @@ -0,0 +1,7 @@ +# root dir of templates +[main] +dir = "templates" + +# Alias for partials. In call, change the start of partial path with one of this, if exist. +[partials] +alias = "./deep/more/deep" diff --git a/tls/Cargo.toml b/tls/Cargo.toml index dcae3d35..6e9be047 100644 --- a/tls/Cargo.toml +++ b/tls/Cargo.toml @@ -1,15 +1,16 @@ [package] name = "tls-example" -version = "0.1.0" +version = "0.2.0" authors = ["Nikolay Kim "] -workspace = "../" +edition = "2018" +workspace = ".." [[bin]] name = "tls-server" path = "src/main.rs" [dependencies] -env_logger = "0.5" +actix-rt = "0.2" +actix-web = { version="1.0.0-alpha.1", features=["ssl"] } +env_logger = "0.6" openssl = { version="0.10" } -actix = "0.7" -actix-web = { version = "0.7", features=["ssl"] } diff --git a/tls/cert.pem b/tls/cert.pem index 159aacea..9a744d16 100644 --- a/tls/cert.pem +++ b/tls/cert.pem @@ -1,31 +1,16 @@ -----BEGIN CERTIFICATE----- -MIIFPjCCAyYCCQDvLYiYD+jqeTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV -UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMRAwDgYDVQQKDAdDb21wYW55MQww -CgYDVQQLDANPcmcxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xODAxMjUx -NzQ2MDFaFw0xOTAxMjUxNzQ2MDFaMGExCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJD -QTELMAkGA1UEBwwCU0YxEDAOBgNVBAoMB0NvbXBhbnkxDDAKBgNVBAsMA09yZzEY -MBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEA2WzIA2IpVR9Tb9EFhITlxuhE5rY2a3S6qzYNzQVgSFggxXEPn8k1 -sQEcer5BfAP986Sck3H0FvB4Bt/I8PwOtUCmhwcc8KtB5TcGPR4fjXnrpC+MIK5U -NLkwuyBDKziYzTdBj8kUFX1WxmvEHEgqToPOZfBgsS71cJAR/zOWraDLSRM54jXy -voLZN4Ti9rQagQrvTQ44Vz5ycDQy7UxtbUGh1CVv69vNVr7/SOOh/Nw5FNOZWLWr -odGyoec5wh9iqRZgRqiTUc6Lt7V2RWc2X2gjwST2UfI+U46Ip3oaQ7ZD4eAkoqND -xdniBZAykVG3c/99ux4BAESTF8fsNch6UticBxYMuTu+ouvP0psfI9wwwNliJDmA -CRUTB9AgRynbL1AzhqQoDfsb98IZfjfNOpwnwuLwpMAPhbgd5KNdZaIJ4Hb6/stI -yFElOExxd3TAxF2Gshd/lq1JcNHAZ1DSXV5MvOWT/NWgXwbIzUgQ8eIi+HuDYX2U -UuaB6R8tbd52H7rbUv6HrfinuSlKWqjSYLkiKHkwUpoMw8y9UycRSzs1E9nPwPTO -vRXb0mNCQeBCV9FvStNVXdCUTT8LGPv87xSD2pmt7LijlE6mHLG8McfcWkzA69un -CEHIFAFDimTuN7EBljc119xWFTcHMyoZAfFF+oTqwSbBGImruCxnaJECAwEAATAN -BgkqhkiG9w0BAQsFAAOCAgEApavsgsn7SpPHfhDSN5iZs1ILZQRewJg0Bty0xPfk -3tynSW6bNH3nSaKbpsdmxxomthNSQgD2heOq1By9YzeOoNR+7Pk3s4FkASnf3ToI -JNTUasBFFfaCG96s4Yvs8KiWS/k84yaWuU8c3Wb1jXs5Rv1qE1Uvuwat1DSGXSoD -JNluuIkCsC4kWkyq5pWCGQrabWPRTWsHwC3PTcwSRBaFgYLJaR72SloHB1ot02zL -d2age9dmFRFLLCBzP+D7RojBvL37qS/HR+rQ4SoQwiVc/JzaeqSe7ZbvEH9sZYEu -ALowJzgbwro7oZflwTWunSeSGDSltkqKjvWvZI61pwfHKDahUTmZ5h2y67FuGEaC -CIOUI8dSVSPKITxaq3JL4ze2e9/0Lt7hj19YK2uUmtMAW5Tirz4Yx5lyGH9U8Wur -y/X8VPxTc4A9TMlJgkyz0hqvhbPOT/zSWB10zXh0glKAsSBryAOEDxV1UygmSir7 -YV8Qaq+oyKUTMc1MFq5vZ07M51EPaietn85t8V2Y+k/8XYltRp32NxsypxAJuyxh -g/ko6RVTrWa1sMvz/F9LFqAdKiK5eM96lh9IU4xiLg4ob8aS/GRAA8oIFkZFhLrt -tOwjIUPmEPyHWFi8dLpNuQKYalLYhuwZftG/9xV+wqhKGZO9iPrpHSYBRTap8w2y -1QU= +MIICljCCAX4CCQDztMNlxk6oeTANBgkqhkiG9w0BAQsFADANMQswCQYDVQQIDAJj +YTAeFw0xOTAzMDcwNzEyNThaFw0yMDAzMDYwNzEyNThaMA0xCzAJBgNVBAgMAmNh +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0GMP3YzDVFWgNhRiHnfe +d192131Zi23p8WiutneD9I5WO42c79fOXsxLWn+2HSqPvCPHIBLoMX8o9lgCxt2P +/JUCAWbrE2EuvhkMrWk6/q7xB211XZYfnkqdt7mA0jMUC5o32AX3ew456TAq5P8Y +dq9H/qXdRtAvKD0QdkFfq8ePCiqOhcqacZ/NWva7R4HdgTnbL1DRQjGBXszI07P9 +1yw8GOym46uxNHRujQp3lYEhc1V3JTF9kETpSBHyEAkQ8WHxGf8UBHDhh7hcc+KI +JHMlVYy5wDv4ZJeYsY1rD6/n4tyd3r0yzBM57UGf6qrVZEYmLB7Jad+8Df5vIoGh +WwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB1DEu9NiShCfQuA17MG5O0Jr2/PS1z +/+HW7oW15WXpqDKOEJalid31/Bzwvwq0bE12xKE4ZLdbqJHmJTdSUoGfOfBZKka6 +R2thOjqH7hFvxjfgS7kBy5BrRZewM9xKIJ6zU6+6mxR64x9vmkOmppV0fx5clZjH +c7qn5kSNWTMsFbjPnb5BeJJwZdqpMLs99jgoMvGtCUmkyVYODGhh65g6tR9kIPvM +zu/Cw122/y7tFfkuknMSYwGEYF3XcZpXt54a6Lu5hk6PuOTsK+7lC+HX7CSF1dpv +u1szL5fDgiCBFCnyKeOqF61mxTCUht3U++37VDFvhzN1t6HIVTYm2JJ7 -----END CERTIFICATE----- diff --git a/tls/key.pem b/tls/key.pem index aac387c6..4416facc 100644 --- a/tls/key.pem +++ b/tls/key.pem @@ -1,51 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEA2WzIA2IpVR9Tb9EFhITlxuhE5rY2a3S6qzYNzQVgSFggxXEP -n8k1sQEcer5BfAP986Sck3H0FvB4Bt/I8PwOtUCmhwcc8KtB5TcGPR4fjXnrpC+M -IK5UNLkwuyBDKziYzTdBj8kUFX1WxmvEHEgqToPOZfBgsS71cJAR/zOWraDLSRM5 -4jXyvoLZN4Ti9rQagQrvTQ44Vz5ycDQy7UxtbUGh1CVv69vNVr7/SOOh/Nw5FNOZ -WLWrodGyoec5wh9iqRZgRqiTUc6Lt7V2RWc2X2gjwST2UfI+U46Ip3oaQ7ZD4eAk -oqNDxdniBZAykVG3c/99ux4BAESTF8fsNch6UticBxYMuTu+ouvP0psfI9wwwNli -JDmACRUTB9AgRynbL1AzhqQoDfsb98IZfjfNOpwnwuLwpMAPhbgd5KNdZaIJ4Hb6 -/stIyFElOExxd3TAxF2Gshd/lq1JcNHAZ1DSXV5MvOWT/NWgXwbIzUgQ8eIi+HuD -YX2UUuaB6R8tbd52H7rbUv6HrfinuSlKWqjSYLkiKHkwUpoMw8y9UycRSzs1E9nP -wPTOvRXb0mNCQeBCV9FvStNVXdCUTT8LGPv87xSD2pmt7LijlE6mHLG8McfcWkzA -69unCEHIFAFDimTuN7EBljc119xWFTcHMyoZAfFF+oTqwSbBGImruCxnaJECAwEA -AQKCAgAME3aoeXNCPxMrSri7u4Xnnk71YXl0Tm9vwvjRQlMusXZggP8VKN/KjP0/ -9AE/GhmoxqPLrLCZ9ZE1EIjgmZ9Xgde9+C8rTtfCG2RFUL7/5J2p6NonlocmxoJm -YkxYwjP6ce86RTjQWL3RF3s09u0inz9/efJk5O7M6bOWMQ9VZXDlBiRY5BYvbqUR -6FeSzD4MnMbdyMRoVBeXE88gTvZk8xhB6DJnLzYgc0tKiRoeKT0iYv5JZw25VyRM -ycLzfTrFmXCPfB1ylb483d9Ly4fBlM8nkx37PzEnAuukIawDxsPOb9yZC+hfvNJI -7NFiMN+3maEqG2iC00w4Lep4skHY7eHUEUMl+Wjr+koAy2YGLWAwHZQTm7iXn9Ab -L6adL53zyCKelRuEQOzbeosJAqS+5fpMK0ekXyoFIuskj7bWuIoCX7K/kg6q5IW+ -vC2FrlsrbQ79GztWLVmHFO1I4J9M5r666YS0qdh8c+2yyRl4FmSiHfGxb3eOKpxQ -b6uI97iZlkxPF9LYUCSc7wq0V2gGz+6LnGvTHlHrOfVXqw/5pLAKhXqxvnroDTwz -0Ay/xFF6ei/NSxBY5t8ztGCBm45wCU3l8pW0X6dXqwUipw5b4MRy1VFRu6rqlmbL -OPSCuLxqyqsigiEYsBgS/icvXz9DWmCQMPd2XM9YhsHvUq+R4QKCAQEA98EuMMXI -6UKIt1kK2t/3OeJRyDd4iv/fCMUAnuPjLBvFE4cXD/SbqCxcQYqb+pue3PYkiTIC -71rN8OQAc5yKhzmmnCE5N26br/0pG4pwEjIr6mt8kZHmemOCNEzvhhT83nfKmV0g -9lNtuGEQMiwmZrpUOF51JOMC39bzcVjYX2Cmvb7cFbIq3lR0zwM+aZpQ4P8LHCIu -bgHmwbdlkLyIULJcQmHIbo6nPFB3ZZE4mqmjwY+rA6Fh9rgBa8OFCfTtrgeYXrNb -IgZQ5U8GoYRPNC2ot0vpTinraboa/cgm6oG4M7FW1POCJTl+/ktHEnKuO5oroSga -/BSg7hCNFVaOhwKCAQEA4Kkys0HtwEbV5mY/NnvUD5KwfXX7BxoXc9lZ6seVoLEc -KjgPYxqYRVrC7dB2YDwwp3qcRTi/uBAgFNm3iYlDzI4xS5SeaudUWjglj7BSgXE2 -iOEa7EwcvVPluLaTgiWjlzUKeUCNNHWSeQOt+paBOT+IgwRVemGVpAgkqQzNh/nP -tl3p9aNtgzEm1qVlPclY/XUCtf3bcOR+z1f1b4jBdn0leu5OhnxkC+Htik+2fTXD -jt6JGrMkanN25YzsjnD3Sn+v6SO26H99wnYx5oMSdmb8SlWRrKtfJHnihphjG/YY -l1cyorV6M/asSgXNQfGJm4OuJi0I4/FL2wLUHnU+JwKCAQEAzh4WipcRthYXXcoj -gMKRkMOb3GFh1OpYqJgVExtudNTJmZxq8GhFU51MR27Eo7LycMwKy2UjEfTOnplh -Us2qZiPtW7k8O8S2m6yXlYUQBeNdq9IuuYDTaYD94vsazscJNSAeGodjE+uGvb1q -1wLqE87yoE7dUInYa1cOA3+xy2/CaNuviBFJHtzOrSb6tqqenQEyQf6h9/12+DTW -t5pSIiixHrzxHiFqOoCLRKGToQB+71rSINwTf0nITNpGBWmSj5VcC3VV3TG5/XxI -fPlxV2yhD5WFDPVNGBGvwPDSh4jSMZdZMSNBZCy4XWFNSKjGEWoK4DFYed3DoSt9 -5IG1YwKCAQA63ntHl64KJUWlkwNbboU583FF3uWBjee5VqoGKHhf3CkKMxhtGqnt -+oN7t5VdUEhbinhqdx1dyPPvIsHCS3K1pkjqii4cyzNCVNYa2dQ00Qq+QWZBpwwc -3GAkz8rFXsGIPMDa1vxpU6mnBjzPniKMcsZ9tmQDppCEpBGfLpio2eAA5IkK8eEf -cIDB3CM0Vo94EvI76CJZabaE9IJ+0HIJb2+jz9BJ00yQBIqvJIYoNy9gP5Xjpi+T -qV/tdMkD5jwWjHD3AYHLWKUGkNwwkAYFeqT/gX6jpWBP+ZRPOp011X3KInJFSpKU -DT5GQ1Dux7EMTCwVGtXqjO8Ym5wjwwsfAoIBAEcxlhIW1G6BiNfnWbNPWBdh3v/K -5Ln98Rcrz8UIbWyl7qNPjYb13C1KmifVG1Rym9vWMO3KuG5atK3Mz2yLVRtmWAVc -fxzR57zz9MZFDun66xo+Z1wN3fVxQB4CYpOEI4Lb9ioX4v85hm3D6RpFukNtRQEc -Gfr4scTjJX4jFWDp0h6ffMb8mY+quvZoJ0TJqV9L9Yj6Ksdvqez/bdSraev97bHQ -4gbQxaTZ6WjaD4HjpPQefMdWp97Metg0ZQSS8b8EzmNFgyJ3XcjirzwliKTAQtn6 -I2sd0NCIooelrKRD8EJoDUwxoOctY7R97wpZ7/wEHU45cBCbRV3H4JILS5c= ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDQYw/djMNUVaA2 +FGIed953X3bXfVmLbenxaK62d4P0jlY7jZzv185ezEtaf7YdKo+8I8cgEugxfyj2 +WALG3Y/8lQIBZusTYS6+GQytaTr+rvEHbXVdlh+eSp23uYDSMxQLmjfYBfd7Djnp +MCrk/xh2r0f+pd1G0C8oPRB2QV+rx48KKo6Fyppxn81a9rtHgd2BOdsvUNFCMYFe +zMjTs/3XLDwY7Kbjq7E0dG6NCneVgSFzVXclMX2QROlIEfIQCRDxYfEZ/xQEcOGH +uFxz4ogkcyVVjLnAO/hkl5ixjWsPr+fi3J3evTLMEzntQZ/qqtVkRiYsHslp37wN +/m8igaFbAgMBAAECggEAJI278rkGany6pcHdlEqik34DcrliQ7r8FoSuYQOF+hgd +uESXCttoL+jWLwHICEW3AOGlxFKMuGH95Xh6xDeJUl0xBN3wzm11rZLnTmPvHU3C +qfLha5Ex6qpcECZSGo0rLv3WXeZuCv/r2KPCYnj86ZTFpD2kGw/Ztc1AXf4Jsi/1 +478Mf23QmAvCAPimGCyjLQx2c9/vg/6K7WnDevY4tDuDKLeSJxKZBSHUn3cM1Bwj +2QzaHfSFA5XljOF5PLeR3cY5ncrrVLWChT9XuGt9YMdLAcSQxgE6kWV1RSCq+lbj +e6OOe879IrrqwBvMQfKQqnm1kl8OrfPMT5CNWKvEgQKBgQD8q5E4x9taDS9RmhRO +07ptsr/I795tX8CaJd/jc4xGuCGBqpNw/hVebyNNYQvpiYzDNBSEhtd59957VyET +hcrGyxD0ByKm8F/lPgFw5y6wi3RUnucCV/jxkMHmxVzYMbFUEGCQ0pIU9/GFS7RZ +9VjqRDeE86U3yHO+WCFoHtd8aQKBgQDTIhi0uq0oY87bUGnWbrrkR0UVRNPDG1BT +cuXACYlv/DV/XpxPC8iPK1UwG4XaOVxodtIRjdBqvb8fUM6HSY6qll64N/4/1jre +Ho+d4clE4tK6a9WU96CKxwHn2BrWUZJPtoldaCZJFJ7SfiHuLlqW7TtYFrOfPIjN +ADiqK+bHIwKBgQCpfIiAVwebo0Z/bWR77+iZFxMwvT4tjdJLVGaXUvXgpjjLmtkm +LTm2S8SZbiSodfz3H+M3dp/pj8wsXiiwyMlZifOITZT/+DPLOUmMK3cVM6ZH8QMy +fkJd/+UhYHhECSlTI10zKByXdi4LZNnIkhwfoLzBMRI9lfeV0dYu2qlfKQKBgEVI +kRbtk1kHt5/ceX62g3nZsV/TYDJMSkW4FJC6EHHBL8UGRQDjewMQUzogLgJ4hEx7 +gV/lS5lbftZF7CAVEU4FXjvRlAtav6KYIMTMjQGf9UrbjBEAWZxwxb1Q+y2NQxgJ +bHZMcRPWQnAMmBHTAEM6whicCoGcmb+77Nxa37ZFAoGBALBuUNeD3fKvQR8v6GoA +spv+RYL9TB4wz2Oe9EYSp9z5EiWlTmuvFz3zk8pHDSpntxYH5O5HJ/3OzwhHz9ym ++DNE9AP9LW9hAzMuu7Gob1h8ShGwJVYwrQN3q/83ooUL7WSAuVOLpzJ7BFFlcCjp +MhFvd9iOt/R0N30/3AbQXkOp +-----END PRIVATE KEY----- diff --git a/tls/src/main.rs b/tls/src/main.rs index 329e15db..7e95309a 100644 --- a/tls/src/main.rs +++ b/tls/src/main.rs @@ -1,26 +1,21 @@ -#![allow(unused_variables)] -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate openssl; +use std::io; -use actix_web::{http, middleware, server, App, Error, HttpRequest, HttpResponse}; +use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; /// simple handle -fn index(req: &HttpRequest) -> Result { +fn index(req: HttpRequest) -> Result { println!("{:?}", req); Ok(HttpResponse::Ok() .content_type("text/plain") .body("Welcome!")) } -fn main() { - if ::std::env::var("RUST_LOG").is_err() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); - } +fn main() -> io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=debug"); env_logger::init(); - let sys = actix::System::new("ws-example"); + + let sys = actix_rt::System::new("tls-example"); // load ssl keys let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); @@ -29,22 +24,22 @@ fn main() { .unwrap(); builder.set_certificate_chain_file("cert.pem").unwrap(); - server::new(|| { + HttpServer::new(|| { App::new() // enable logger - .middleware(middleware::Logger::default()) + .wrap(middleware::Logger::default()) // register simple handler, handle all methods - .resource("/index.html", |r| r.f(index)) + .service(web::resource("/index.html").to(index)) // with path parameters - .resource("/", |r| r.method(http::Method::GET).f(|req| { + .service(web::resource("/").route(web::get().to(|| { HttpResponse::Found() .header("LOCATION", "/index.html") .finish() - })) - }).bind_ssl("127.0.0.1:8443", builder) - .unwrap() - .start(); + }))) + }) + .bind_ssl("127.0.0.1:8443", builder)? + .start(); println!("Started http server: 127.0.0.1:8443"); - let _ = sys.run(); + sys.run() } diff --git a/unix-socket/src/main.rs b/unix-socket/src/main.rs index d999f8e9..6a3e3ed3 100644 --- a/unix-socket/src/main.rs +++ b/unix-socket/src/main.rs @@ -23,7 +23,8 @@ fn main() { .middleware(middleware::Logger::default()) .resource("/index.html", |r| r.f(|_| "Hello world!")) .resource("/", |r| r.f(index)) - }).start_incoming(listener.incoming(), false); + }) + .start_incoming(listener.incoming(), false); println!("Started http server: /tmp/actix-uds.socket"); let _ = sys.run(); diff --git a/web-cors/backend/Cargo.toml b/web-cors/backend/Cargo.toml index f78d7adc..c50eb197 100644 --- a/web-cors/backend/Cargo.toml +++ b/web-cors/backend/Cargo.toml @@ -3,16 +3,13 @@ name = "actix-web-cors" version = "0.1.0" authors = ["krircc "] workspace = "../../" +edition = "2018" [dependencies] +actix-web = "1.0.0-alpha.1" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -http = "0.1" - -actix = "0.7" -actix-web = "0.7" - dotenv = "0.10" -env_logger = "0.5" +env_logger = "0.6" futures = "0.1" diff --git a/web-cors/backend/src/main.rs b/web-cors/backend/src/main.rs index 8191ceeb..5d951e03 100644 --- a/web-cors/backend/src/main.rs +++ b/web-cors/backend/src/main.rs @@ -1,48 +1,29 @@ #[macro_use] extern crate serde_derive; -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate futures; -extern crate serde; -extern crate serde_json; use actix_web::{ - http::{header, Method}, - middleware, - middleware::cors::Cors, - server, App, + http::header, middleware::cors::Cors, middleware::Logger, web, App, HttpServer, }; -use std::env; mod user; -use user::info; -fn main() { - env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix::System::new("Actix-web-CORS"); - - server::new(move || { + HttpServer::new(move || { App::new() - .middleware(middleware::Logger::default()) - .configure(|app| { - Cors::for_app(app) + .wrap( + Cors::new() .allowed_origin("http://localhost:1234") .allowed_methods(vec!["GET", "POST"]) .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) .allowed_header(header::CONTENT_TYPE) - .max_age(3600) - .resource("/user/info", |r| { - r.method(Method::POST).with(info); - }) - .register() - }) - }).bind("127.0.0.1:8000") - .unwrap() - .shutdown_timeout(2) - .start(); - - let _ = sys.run(); + .max_age(3600), + ) + .wrap(Logger::default()) + .service(web::resource("/user/info").route(web::post().to(user::info))) + }) + .bind("127.0.0.1:8000")? + .run() } diff --git a/web-cors/backend/src/user.rs b/web-cors/backend/src/user.rs index aca44cbf..ed795589 100644 --- a/web-cors/backend/src/user.rs +++ b/web-cors/backend/src/user.rs @@ -1,4 +1,4 @@ -use actix_web::{Json, Result}; +use actix_web::web; #[derive(Deserialize, Serialize, Debug)] pub struct Info { @@ -8,12 +8,12 @@ pub struct Info { confirm_password: String, } -pub fn info(info: Json) -> Result> { +pub fn info(info: web::Json) -> web::Json { println!("=========={:?}=========", info); - Ok(Json(Info { + web::Json(Info { username: info.username.clone(), email: info.email.clone(), password: info.password.clone(), confirm_password: info.confirm_password.clone(), - })) + }) } diff --git a/websocket-chat-broker/src/main.rs b/websocket-chat-broker/src/main.rs index 24ae4929..4ddc3bc7 100644 --- a/websocket-chat-broker/src/main.rs +++ b/websocket-chat-broker/src/main.rs @@ -52,7 +52,8 @@ impl WsChatSession { } fut::ok(()) - }).spawn(ctx); + }) + .spawn(ctx); } fn list_rooms(&mut self, ctx: &mut ws::WebsocketContext) { @@ -66,7 +67,8 @@ impl WsChatSession { } } fut::ok(()) - }).spawn(ctx); + }) + .spawn(ctx); } fn send_msg(&self, msg: &str) { @@ -158,7 +160,8 @@ fn main() { .unwrap() .index_file("index.html"), ) - }).bind("127.0.0.1:8080") + }) + .bind("127.0.0.1:8080") .unwrap() .start(); diff --git a/websocket-chat/Cargo.toml b/websocket-chat/Cargo.toml index b6d281ff..64c7876e 100644 --- a/websocket-chat/Cargo.toml +++ b/websocket-chat/Cargo.toml @@ -2,23 +2,24 @@ name = "websocket-example" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [[bin]] name = "websocket-chat-server" path = "src/main.rs" [dependencies] -rand = "*" +actix = "0.8.0-alpha.2" +actix-web = "1.0.0-alpha.1" +actix-web-actors = "1.0.0-alpha.1" +actix-files = "0.1.0-alpha.1" + +rand = "0.6" bytes = "0.4" byteorder = "1.1" -futures = "0.1" +futures = "0.1.25" tokio-io = "0.1" -tokio-core = "0.1" -env_logger = "*" - +env_logger = "0.6" serde = "1.0" serde_json = "1.0" - -actix = "0.7" -actix-web = "0.7" diff --git a/websocket-chat/src/main.rs b/websocket-chat/src/main.rs index 477b8e40..45085f7c 100644 --- a/websocket-chat/src/main.rs +++ b/websocket-chat/src/main.rs @@ -1,22 +1,9 @@ -#![allow(unused_variables)] -extern crate byteorder; -extern crate bytes; -extern crate env_logger; -extern crate futures; -extern crate rand; -extern crate serde; -extern crate serde_json; -extern crate tokio_core; -extern crate tokio_io; - -extern crate actix; -extern crate actix_web; - -use std::time::{Instant, Duration}; +use std::time::{Duration, Instant}; use actix::*; -use actix_web::server::HttpServer; -use actix_web::{fs, http, ws, App, Error, HttpRequest, HttpResponse}; +use actix_files as fs; +use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer}; +use actix_web_actors::ws; mod server; @@ -25,22 +12,22 @@ const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); /// How long before lack of client response causes a timeout const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); -/// This is our websocket route state, this state is shared with all route -/// instances via `HttpContext::state()` -struct WsChatSessionState { - addr: Addr, -} - /// Entry point for our route -fn chat_route(req: &HttpRequest) -> Result { +fn chat_route( + req: HttpRequest, + stream: web::Payload, + srv: web::Data>, +) -> Result { ws::start( - req, WsChatSession { id: 0, hb: Instant::now(), room: "Main".to_owned(), name: None, + addr: srv.get_ref().clone(), }, + &req, + stream, ) } @@ -54,10 +41,12 @@ struct WsChatSession { room: String, /// peer name name: Option, + /// Chat server + addr: Addr, } impl Actor for WsChatSession { - type Context = ws::WebsocketContext; + type Context = ws::WebsocketContext; /// Method is called on actor start. /// We register ws session with ChatServer @@ -71,8 +60,7 @@ impl Actor for WsChatSession { // HttpContext::state() is instance of WsChatSessionState, state is shared // across all routes within application let addr = ctx.address(); - ctx.state() - .addr + self.addr .send(server::Connect { addr: addr.recipient(), }) @@ -88,9 +76,9 @@ impl Actor for WsChatSession { .wait(ctx); } - fn stopping(&mut self, ctx: &mut Self::Context) -> Running { + fn stopping(&mut self, _: &mut Self::Context) -> Running { // notify chat server - ctx.state().addr.do_send(server::Disconnect { id: self.id }); + self.addr.do_send(server::Disconnect { id: self.id }); Running::Stop } } @@ -126,8 +114,7 @@ impl StreamHandler for WsChatSession { // Send ListRooms message to chat server and wait for // response println!("List rooms"); - ctx.state() - .addr + self.addr .send(server::ListRooms) .into_actor(self) .then(|res, _, ctx| { @@ -149,7 +136,7 @@ impl StreamHandler for WsChatSession { "/join" => { if v.len() == 2 { self.room = v[1].to_owned(); - ctx.state().addr.do_send(server::Join { + self.addr.do_send(server::Join { id: self.id, name: self.room.clone(), }); @@ -175,17 +162,17 @@ impl StreamHandler for WsChatSession { m.to_owned() }; // send message to chat server - ctx.state().addr.do_send(server::ClientMessage { + self.addr.do_send(server::ClientMessage { id: self.id, msg: msg, room: self.room.clone(), }) } } - ws::Message::Binary(bin) => println!("Unexpected binary"), + ws::Message::Binary(_) => println!("Unexpected binary"), ws::Message::Close(_) => { ctx.stop(); - }, + } } } } @@ -194,7 +181,7 @@ impl WsChatSession { /// helper method that sends ping to client every second. /// /// also this method checks heartbeats from client - fn hb(&self, ctx: &mut ws::WebsocketContext) { + fn hb(&self, ctx: &mut ws::WebsocketContext) { ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { // check client heartbeats if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { @@ -202,9 +189,7 @@ impl WsChatSession { println!("Websocket Client heartbeat failed, disconnecting!"); // notify chat server - ctx.state() - .addr - .do_send(server::Disconnect { id: act.id }); + act.addr.do_send(server::Disconnect { id: act.id }); // stop actor ctx.stop(); @@ -218,35 +203,30 @@ impl WsChatSession { } } -fn main() { - let _ = env_logger::init(); - let sys = actix::System::new("websocket-example"); +fn main() -> std::io::Result<()> { + env_logger::init(); + let sys = System::new("ws-example"); - // Start chat server actor in separate thread - let server = Arbiter::start(|_| server::ChatServer::default()); + // Start chat server actor + let server = server::ChatServer::default().start(); // Create Http server with websocket support HttpServer::new(move || { - // Websocket sessions state - let state = WsChatSessionState { - addr: server.clone(), - }; - - App::with_state(state) - // redirect to websocket.html - .resource("/", |r| r.method(http::Method::GET).f(|_| { + App::new() + .data(server.clone()) + // redirect to websocket.html + .service(web::resource("/").route(web::get().to(|| { HttpResponse::Found() .header("LOCATION", "/static/websocket.html") .finish() - })) - // websocket - .resource("/ws/", |r| r.route().f(chat_route)) - // static resources - .handler("/static/", fs::StaticFiles::new("static/").unwrap()) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); + }))) + // websocket + .service(web::resource("/ws/").to(chat_route)) + // static resources + .service(fs::Files::new("/static/", "static/")) + }) + .bind("127.0.0.1:8080")? + .start(); - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + sys.run() } diff --git a/websocket-tcp-chat/Cargo.toml b/websocket-tcp-chat/Cargo.toml index c73d5678..b4a06eb7 100644 --- a/websocket-tcp-chat/Cargo.toml +++ b/websocket-tcp-chat/Cargo.toml @@ -2,7 +2,8 @@ name = "websocket-tcp-example" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +workspace = ".." +edition = "2018" [[bin]] name = "websocket-tcp-server" @@ -13,18 +14,19 @@ name = "websocket-tcp-client" path = "src/client.rs" [dependencies] -rand = "*" +actix = "0.8.0-alpha.2" +actix-web = "1.0.0-alpha.1" +actix-web-actors = "1.0.0-alpha.1" +actix-files = "0.1.0-alpha.1" + +rand = "0.6" bytes = "0.4" byteorder = "1.1" futures = "0.1" tokio-io = "0.1" tokio-tcp = "0.1" tokio-codec = "0.1" -env_logger = "*" - +env_logger = "0.6" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" - -actix = "0.7" -actix-web = "0.7" diff --git a/websocket-tcp-chat/src/codec.rs b/websocket-tcp-chat/src/codec.rs index 55cf0db3..8d362630 100644 --- a/websocket-tcp-chat/src/codec.rs +++ b/websocket-tcp-chat/src/codec.rs @@ -65,7 +65,9 @@ impl Encoder for ChatCodec { type Error = io::Error; fn encode( - &mut self, msg: ChatResponse, dst: &mut BytesMut, + &mut self, + msg: ChatResponse, + dst: &mut BytesMut, ) -> Result<(), Self::Error> { let msg = json::to_string(&msg).unwrap(); let msg_ref: &[u8] = msg.as_ref(); @@ -108,7 +110,9 @@ impl Encoder for ClientChatCodec { type Error = io::Error; fn encode( - &mut self, msg: ChatRequest, dst: &mut BytesMut, + &mut self, + msg: ChatRequest, + dst: &mut BytesMut, ) -> Result<(), Self::Error> { let msg = json::to_string(&msg).unwrap(); let msg_ref: &[u8] = msg.as_ref(); diff --git a/websocket-tcp-chat/src/main.rs b/websocket-tcp-chat/src/main.rs index 099ba8d4..cd96c40b 100644 --- a/websocket-tcp-chat/src/main.rs +++ b/websocket-tcp-chat/src/main.rs @@ -1,25 +1,14 @@ -#![allow(unused_variables)] -extern crate byteorder; -extern crate bytes; -extern crate env_logger; -extern crate futures; -extern crate rand; -extern crate serde; -extern crate serde_json; -extern crate tokio_codec; -extern crate tokio_io; -extern crate tokio_tcp; #[macro_use] extern crate serde_derive; - #[macro_use] extern crate actix; -extern crate actix_web; + +use std::time::{Duration, Instant}; use actix::*; -use actix_web::server::HttpServer; -use actix_web::{fs, http, ws, App, Error, HttpRequest, HttpResponse}; -use std::time::{Instant, Duration}; +use actix_files as fs; +use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer}; +use actix_web_actors::ws; mod codec; mod server; @@ -30,22 +19,22 @@ const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); /// How long before lack of client response causes a timeout const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); -/// This is our websocket route state, this state is shared with all route -/// instances via `HttpContext::state()` -struct WsChatSessionState { - addr: Addr, -} - /// Entry point for our route -fn chat_route(req: &HttpRequest) -> Result { +fn chat_route( + req: HttpRequest, + stream: web::Payload, + srv: web::Data>, +) -> Result { ws::start( - req, WsChatSession { id: 0, hb: Instant::now(), room: "Main".to_owned(), name: None, + addr: srv.get_ref().clone(), }, + &req, + stream, ) } @@ -59,26 +48,26 @@ struct WsChatSession { room: String, /// peer name name: Option, + /// Chat server + addr: Addr, } impl Actor for WsChatSession { - type Context = ws::WebsocketContext; + type Context = ws::WebsocketContext; /// Method is called on actor start. /// We register ws session with ChatServer fn started(&mut self, ctx: &mut Self::Context) { + // we'll start heartbeat process on session start. + self.hb(ctx); + // register self in chat server. `AsyncContext::wait` register // future within context, but context waits until this future resolves // before processing any other events. // HttpContext::state() is instance of WsChatSessionState, state is shared // across all routes within application - - // we'll start heartbeat process on session start. - self.hb(ctx); - let addr = ctx.address(); - ctx.state() - .addr + self.addr .send(server::Connect { addr: addr.recipient(), }) @@ -94,9 +83,9 @@ impl Actor for WsChatSession { .wait(ctx); } - fn stopping(&mut self, ctx: &mut Self::Context) -> Running { + fn stopping(&mut self, _: &mut Self::Context) -> Running { // notify chat server - ctx.state().addr.do_send(server::Disconnect { id: self.id }); + self.addr.do_send(server::Disconnect { id: self.id }); Running::Stop } } @@ -132,8 +121,7 @@ impl StreamHandler for WsChatSession { // Send ListRooms message to chat server and wait for // response println!("List rooms"); - ctx.state() - .addr + self.addr .send(server::ListRooms) .into_actor(self) .then(|res, _, ctx| { @@ -155,7 +143,7 @@ impl StreamHandler for WsChatSession { "/join" => { if v.len() == 2 { self.room = v[1].to_owned(); - ctx.state().addr.do_send(server::Join { + self.addr.do_send(server::Join { id: self.id, name: self.room.clone(), }); @@ -181,17 +169,17 @@ impl StreamHandler for WsChatSession { m.to_owned() }; // send message to chat server - ctx.state().addr.do_send(server::Message { + self.addr.do_send(server::Message { id: self.id, msg: msg, room: self.room.clone(), }) } } - ws::Message::Binary(bin) => println!("Unexpected binary"), + ws::Message::Binary(_) => println!("Unexpected binary"), ws::Message::Close(_) => { ctx.stop(); - }, + } } } } @@ -200,7 +188,7 @@ impl WsChatSession { /// helper method that sends ping to client every second. /// /// also this method checks heartbeats from client - fn hb(&self, ctx: &mut ws::WebsocketContext) { + fn hb(&self, ctx: &mut ws::WebsocketContext) { ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { // check client heartbeats if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { @@ -208,9 +196,7 @@ impl WsChatSession { println!("Websocket Client heartbeat failed, disconnecting!"); // notify chat server - ctx.state() - .addr - .do_send(server::Disconnect { id: act.id }); + act.addr.do_send(server::Disconnect { id: act.id }); // stop actor ctx.stop(); @@ -224,42 +210,38 @@ impl WsChatSession { } } -fn main() { +fn main() -> std::io::Result<()> { let _ = env_logger::init(); let sys = actix::System::new("websocket-example"); - // Start chat server actor in separate thread - let server = Arbiter::start(|_| server::ChatServer::default()); + // Start chat server actor + let server = server::ChatServer::default().start(); // Start tcp server in separate thread let srv = server.clone(); - Arbiter::new("tcp-server").do_send::(msgs::Execute::new(move || { + Arbiter::new().exec(move || { session::TcpServer::new("127.0.0.1:12345", srv); - Ok(()) - })); + Ok::<_, ()>(()) + }); // Create Http server with websocket support HttpServer::new(move || { - // Websocket sessions state - let state = WsChatSessionState { - addr: server.clone(), - }; - - App::with_state(state) + App::new() + .data(server.clone()) // redirect to websocket.html - .resource("/", |r| r.method(http::Method::GET).f(|_| { + .service(web::resource("/").route(web::get().to(|| { HttpResponse::Found() .header("LOCATION", "/static/websocket.html") .finish() - })) + }))) // websocket - .resource("/ws/", |r| r.route().f(chat_route)) + .service(web::resource("/ws/").to(chat_route)) // static resources - .handler("/static/", fs::StaticFiles::new("static/").unwrap()) - }).bind("127.0.0.1:8080") - .unwrap() - .start(); + .service(fs::Files::new("/static/", "static/")) + }) + .bind("127.0.0.1:8080")? + .start(); println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + sys.run() } diff --git a/websocket-tcp-chat/src/server.rs b/websocket-tcp-chat/src/server.rs index efc946a8..c30b928b 100644 --- a/websocket-tcp-chat/src/server.rs +++ b/websocket-tcp-chat/src/server.rs @@ -6,7 +6,7 @@ use actix::prelude::*; use rand::{self, rngs::ThreadRng, Rng}; use std::collections::{HashMap, HashSet}; -use session; +use crate::session; /// Message for chat server communications diff --git a/websocket-tcp-chat/src/session.rs b/websocket-tcp-chat/src/session.rs index 37bd33a9..27478794 100644 --- a/websocket-tcp-chat/src/session.rs +++ b/websocket-tcp-chat/src/session.rs @@ -11,8 +11,8 @@ use tokio_tcp::{TcpListener, TcpStream}; use actix::prelude::*; -use codec::{ChatCodec, ChatRequest, ChatResponse}; -use server::{self, ChatServer}; +use crate::codec::{ChatCodec, ChatRequest, ChatResponse}; +use crate::server::{self, ChatServer}; /// Chat server sends this messages to session #[derive(Message)] @@ -62,7 +62,7 @@ impl Actor for ChatSession { .wait(ctx); } - fn stopping(&mut self, ctx: &mut Self::Context) -> Running { + fn stopping(&mut self, _: &mut Self::Context) -> Running { // notify chat server self.addr.do_send(server::Disconnect { id: self.id }); Running::Stop @@ -82,7 +82,7 @@ impl StreamHandler for ChatSession { self.addr .send(server::ListRooms) .into_actor(self) - .then(|res, act, ctx| { + .then(|res, act, _| { match res { Ok(rooms) => { act.framed.write(ChatResponse::Rooms(rooms)); @@ -124,7 +124,7 @@ impl StreamHandler for ChatSession { impl Handler for ChatSession { type Result = (); - fn handle(&mut self, msg: Message, ctx: &mut Context) { + fn handle(&mut self, msg: Message, _: &mut Context) { // send message to peer self.framed.write(ChatResponse::Message(msg.0)); } @@ -175,7 +175,7 @@ pub struct TcpServer { } impl TcpServer { - pub fn new(s: &str, chat: Addr) { + pub fn new(_s: &str, chat: Addr) { // Create server listener let addr = net::SocketAddr::from_str("127.0.0.1:12345").unwrap(); let listener = TcpListener::bind(&addr).unwrap(); diff --git a/websocket/Cargo.toml b/websocket/Cargo.toml index 4ed656ec..2ceab26e 100644 --- a/websocket/Cargo.toml +++ b/websocket/Cargo.toml @@ -2,19 +2,22 @@ name = "websocket" version = "0.1.0" authors = ["Nikolay Kim "] -workspace = "../" +edition = "2018" +workspace = ".." [[bin]] name = "websocket-server" path = "src/main.rs" -[[bin]] -name = "websocket-client" -path = "src/client.rs" +#[[bin]] +#name = "websocket-client" +#path = "src/client.rs" [dependencies] -env_logger = "*" +actix = "0.8.0-alpha.1" +actix-web = "1.0.0-alpha.1" +actix-web-actors = "1.0.0-alpha.1" +actix-files = "0.1.0-alpha.1" +env_logger = "0.6" futures = "0.1" - -actix = "0.7" -actix-web = "0.7" +bytes = "0.4" \ No newline at end of file diff --git a/websocket/src/client.rs b/websocket/src/client.rs index 7cb5fa3b..11f827bb 100644 --- a/websocket/src/client.rs +++ b/websocket/src/client.rs @@ -1,16 +1,9 @@ //! Simple websocket client. - -#![allow(unused_variables)] -extern crate actix; -extern crate actix_web; -extern crate env_logger; -extern crate futures; - use std::time::Duration; use std::{io, thread}; use actix::*; -use actix_web::ws::{Client, ClientWriter, Message, ProtocolError}; +use actix_web::client::{Client, ClientWriter, Message, ProtocolError}; use futures::Future; fn main() { diff --git a/websocket/src/main.rs b/websocket/src/main.rs index 84aa4646..67d57918 100644 --- a/websocket/src/main.rs +++ b/websocket/src/main.rs @@ -3,17 +3,12 @@ //! or [python console client](https://github.com/actix/examples/blob/master/websocket/websocket-client.py) //! could be used for testing. -#![allow(unused_variables)] -extern crate actix; -extern crate actix_web; -extern crate env_logger; - -use std::time::{Instant, Duration}; +use std::time::{Duration, Instant}; use actix::prelude::*; -use actix_web::{ - fs, http, middleware, server, ws, App, Error, HttpRequest, HttpResponse, -}; +use actix_files as fs; +use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; +use actix_web_actors::ws; /// How often heartbeat pings are sent const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); @@ -21,8 +16,11 @@ const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); /// do websocket handshake and start `MyWebSocket` actor -fn ws_index(r: &HttpRequest) -> Result { - ws::start(r, MyWebSocket::new()) +fn ws_index(r: HttpRequest, stream: web::Payload) -> Result { + println!("{:?}", r); + let res = ws::start(MyWebSocket::new(), &r, stream); + println!("{:?}", res.as_ref().unwrap()); + res } /// websocket connection is long running connection, it easier @@ -91,25 +89,20 @@ impl MyWebSocket { } } -fn main() { - ::std::env::set_var("RUST_LOG", "actix_web=info"); +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); env_logger::init(); - let sys = actix::System::new("ws-example"); - server::new( - || App::new() + HttpServer::new(|| { + App::new() // enable logger - .middleware(middleware::Logger::default()) + .wrap(middleware::Logger::default()) // websocket route - .resource("/ws/", |r| r.method(http::Method::GET).f(ws_index)) + .service(web::resource("/ws/").route(web::get().to(ws_index))) // static files - .handler("/", fs::StaticFiles::new("static/") - .unwrap() - .index_file("index.html"))) - // start http server on 127.0.0.1:8080 - .bind("127.0.0.1:8080").unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); + .service(fs::Files::new("/", "static/").index_file("index.html")) + }) + // start http server on 127.0.0.1:8080 + .bind("127.0.0.1:8080")? + .run() }