1
0
mirror of https://github.com/actix/examples synced 2024-11-23 22:41:07 +01:00

update more examples and rustfmt

This commit is contained in:
Nikolay Kim 2019-03-09 18:03:09 -08:00
parent f39a53ea3a
commit e2945b9b39
48 changed files with 485 additions and 801 deletions

View File

@ -7,7 +7,6 @@ members = [
"async_ex1", "async_ex1",
"basics", "basics",
"cookie-auth", "cookie-auth",
"cookie-auth-full",
"cookie-session", "cookie-session",
"diesel", "diesel",
"error_handling", "error_handling",

View File

@ -3,30 +3,32 @@ extern crate actix_redis;
extern crate actix_web; extern crate actix_web;
extern crate env_logger; extern crate env_logger;
extern crate futures; extern crate futures;
#[macro_use] extern crate redis_async; #[macro_use]
extern crate redis_async;
extern crate serde; extern crate serde;
#[macro_use] extern crate serde_derive; #[macro_use]
extern crate serde_derive;
use std::sync::Arc;
use actix::prelude::*; use actix::prelude::*;
use actix_redis::{Command, RedisActor, Error as ARError}; use actix_redis::{Command, Error as ARError, RedisActor};
use actix_web::{middleware, server, App, HttpRequest, HttpResponse, Json, use actix_web::{
AsyncResponder, http::Method, Error as AWError}; http::Method, middleware, server, App, AsyncResponder, Error as AWError,
use futures::future::{Future, join_all}; HttpRequest, HttpResponse, Json,
};
use futures::future::{join_all, Future};
use redis_async::resp::RespValue; use redis_async::resp::RespValue;
use std::sync::Arc;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct CacheInfo { pub struct CacheInfo {
one: String, one: String,
two: String, two: String,
three: String three: String,
} }
fn cache_stuff(
fn cache_stuff((info, req): (Json<CacheInfo>, HttpRequest<AppState>)) (info, req): (Json<CacheInfo>, HttpRequest<AppState>),
-> impl Future<Item=HttpResponse, Error=AWError> { ) -> impl Future<Item = HttpResponse, Error = AWError> {
let info = info.into_inner(); let info = info.into_inner();
let redis = req.state().redis_addr.clone(); let redis = req.state().redis_addr.clone();
@ -59,25 +61,33 @@ fn cache_stuff((info, req): (Json<CacheInfo>, HttpRequest<AppState>))
.responder() .responder()
} }
fn del_stuff(req: HttpRequest<AppState>) fn del_stuff(
-> impl Future<Item=HttpResponse, Error=AWError> { req: HttpRequest<AppState>,
) -> impl Future<Item = HttpResponse, Error = AWError> {
let redis = req.state().redis_addr.clone(); let redis = req.state().redis_addr.clone();
redis.send(Command(resp_array!["DEL", "mydomain:one", "mydomain:two", "mydomain:three"])) redis
.send(Command(resp_array![
"DEL",
"mydomain:one",
"mydomain:two",
"mydomain:three"
]))
.map_err(AWError::from) .map_err(AWError::from)
.and_then(|res: Result<RespValue, ARError>| .and_then(|res: Result<RespValue, ARError>| match &res {
match &res { Ok(RespValue::Integer(x)) if x == &3 => {
Ok(RespValue::Integer(x)) if x==&3 => Ok(HttpResponse::Ok().body("successfully deleted values"))
Ok(HttpResponse::Ok().body("successfully deleted values")), }
_ =>{println!("---->{:?}", res); _ => {
Ok(HttpResponse::InternalServerError().finish())} println!("---->{:?}", res);
Ok(HttpResponse::InternalServerError().finish())
}
}) })
.responder() .responder()
} }
pub struct AppState { pub struct AppState {
pub redis_addr: Arc<Addr<RedisActor>> pub redis_addr: Arc<Addr<RedisActor>>,
} }
fn main() { fn main() {
@ -92,12 +102,11 @@ fn main() {
App::with_state(app_state) App::with_state(app_state)
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
.resource("/stuff", |r| { .resource("/stuff", |r| {
r.method(Method::POST) r.method(Method::POST).with_async(cache_stuff);
.with_async(cache_stuff); r.method(Method::DELETE).with_async(del_stuff)
r.method(Method::DELETE) })
.with_async(del_stuff)}) })
.bind("0.0.0.0:8080")
}).bind("0.0.0.0:8080")
.unwrap() .unwrap()
.workers(1) .workers(1)
.start(); .start();

View File

@ -32,7 +32,8 @@ pub fn index(req: HttpRequest<AppState>) -> FutureResponse<HttpResponse> {
session::clear_flash(&req); session::clear_flash(&req);
} }
let rendered = req.state() let rendered = req
.state()
.template .template
.render("index.html.tera", &context) .render("index.html.tera", &context)
.map_err(|e| { .map_err(|e| {
@ -61,7 +62,8 @@ pub fn create(
FlashMessage::error("Description cannot be empty"), FlashMessage::error("Description cannot be empty"),
)?; )?;
Ok(redirect_to("/")) Ok(redirect_to("/"))
}).responder() })
.responder()
} else { } else {
req.state() req.state()
.db .db

View File

@ -3,7 +3,8 @@ use diesel::pg::PgConnection;
use diesel::prelude::*; use diesel::prelude::*;
use schema::{ use schema::{
tasks, tasks::dsl::{completed as task_completed, tasks as all_tasks}, tasks,
tasks::dsl::{completed as task_completed, tasks as all_tasks},
}; };
#[derive(Debug, Insertable)] #[derive(Debug, Insertable)]

View File

@ -2,10 +2,13 @@
name = "awc_examples" name = "awc_examples"
version = "0.1.0" version = "0.1.0"
authors = ["dowwie <dkcdkg@gmail.com>"] authors = ["dowwie <dkcdkg@gmail.com>"]
edition = "2018"
workspace = ".."
[dependencies] [dependencies]
actix = "0.7" actix-rt = "0.2"
actix-web = { version="0.7.3", features=["rust-tls"] } actix-http = { git="https://github.com/actix/actix-http.git", features=["ssl"] }
actix-web = { git="https://github.com/actix/actix-web.git", branch = "1.0", features=["ssl"] }
futures = "0.1" futures = "0.1"
serde = "1.0.43" serde = "1.0.43"

View File

@ -15,25 +15,17 @@
// There are 2 versions in this example, one that uses Boxed Futures and the // There are 2 versions in this example, one that uses Boxed Futures and the
// other that uses Impl Future, available since rustc v1.26. // 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] #[macro_use]
extern crate validator_derive; extern crate validator_derive;
extern crate env_logger; #[macro_use]
extern crate futures; extern crate serde_derive;
extern crate validator;
use actix_web::{
client, http::Method, server, App, AsyncResponder, Error, HttpMessage, HttpResponse,
Json,
};
use futures::{future::ok as fut_ok, Future};
use std::collections::HashMap; use std::collections::HashMap;
use std::time::Duration; use std::io;
use actix_http::client;
use actix_web::{web, App, Error, HttpMessage, HttpResponse, HttpServer};
use futures::future::{ok, Future};
use validator::Validate; use validator::Validate;
#[derive(Debug, Validate, Deserialize, Serialize)] #[derive(Debug, Validate, Deserialize, Serialize)]
@ -62,28 +54,30 @@ struct HttpBinResponse {
/// post json to httpbin, get it back in the response body, return deserialized /// post json to httpbin, get it back in the response body, return deserialized
fn step_x_v1(data: SomeData) -> Box<Future<Item = SomeData, Error = Error>> { fn step_x_v1(data: SomeData) -> Box<Future<Item = SomeData, Error = Error>> {
let mut connector = client::Connector::default().service();
Box::new( Box::new(
client::ClientRequest::post("https://httpbin.org/post") client::ClientRequest::post("https://httpbin.org/post")
.json(data).unwrap() .json(data)
.send() .unwrap()
.conn_timeout(Duration::from_secs(10)) .send(&mut connector)
.map_err(Error::from) // <- convert SendRequestError to an Error .map_err(Error::from) // <- convert SendRequestError to an Error
.and_then( .and_then(|mut resp| {
|resp| resp.body() // <- this is MessageBody type, resolves to complete body resp.body() // <- this is MessageBody type, resolves to complete body
.from_err() // <- convert PayloadError to an Error .from_err() // <- convert PayloadError to an Error
.and_then(|body| { .and_then(|body| {
let resp: HttpBinResponse = serde_json::from_slice(&body).unwrap(); let resp: HttpBinResponse =
fut_ok(resp.json) serde_json::from_slice(&body).unwrap();
ok(resp.json)
}) })
), }),
) )
} }
fn create_something_v1( fn create_something_v1(
some_data: Json<SomeData>, some_data: web::Json<SomeData>,
) -> Box<Future<Item = HttpResponse, Error = Error>> { ) -> Box<Future<Item = HttpResponse, Error = Error>> {
step_x_v1(some_data.into_inner()) Box::new(step_x_v1(some_data.into_inner()).and_then(|some_data_2| {
.and_then(|some_data_2| {
step_x_v1(some_data_2).and_then(|some_data_3| { step_x_v1(some_data_2).and_then(|some_data_3| {
step_x_v1(some_data_3).and_then(|d| { step_x_v1(some_data_3).and_then(|d| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -92,8 +86,7 @@ fn create_something_v1(
.into()) .into())
}) })
}) })
}) }))
.responder()
} }
// --------------------------------------------------------------- // ---------------------------------------------------------------
@ -102,23 +95,25 @@ fn create_something_v1(
/// post json to httpbin, get it back in the response body, return deserialized /// post json to httpbin, get it back in the response body, return deserialized
fn step_x_v2(data: SomeData) -> impl Future<Item = SomeData, Error = Error> { fn step_x_v2(data: SomeData) -> impl Future<Item = SomeData, Error = Error> {
let mut connector = client::Connector::default().service();
client::ClientRequest::post("https://httpbin.org/post") client::ClientRequest::post("https://httpbin.org/post")
.json(data).unwrap() .json(data)
.send() .unwrap()
.conn_timeout(Duration::from_secs(10)) .send(&mut connector)
.map_err(Error::from) // <- convert SendRequestError to an Error .map_err(Error::from) // <- convert SendRequestError to an Error
.and_then( .and_then(|mut resp| {
|resp| resp.body() // <- this is MessageBody type, resolves to complete body resp.body() // <- this is MessageBody type, resolves to complete body
.from_err() // <- convert PayloadError to an Error .from_err() // <- convert PayloadError to an Error
.and_then(|body| { .and_then(|body| {
let resp: HttpBinResponse = serde_json::from_slice(&body).unwrap(); let resp: HttpBinResponse = serde_json::from_slice(&body).unwrap();
fut_ok(resp.json) ok(resp.json)
})
}) })
)
} }
fn create_something_v2( fn create_something_v2(
some_data: Json<SomeData>, some_data: web::Json<SomeData>,
) -> impl Future<Item = HttpResponse, Error = Error> { ) -> impl Future<Item = HttpResponse, Error = Error> {
step_x_v2(some_data.into_inner()).and_then(|some_data_2| { 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_2).and_then(|some_data_3| {
@ -132,23 +127,21 @@ fn create_something_v2(
}) })
} }
fn main() { fn main() -> io::Result<()> {
::std::env::set_var("RUST_LOG", "actix_web=info"); std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init(); env_logger::init();
let sys = actix::System::new("asyncio_example");
server::new(move || { HttpServer::new(|| {
App::new() App::new()
.resource("/something_v1", |r| { .service(
r.method(Method::POST).with(create_something_v1) web::resource("/something_v1")
.route(web::post().to(create_something_v1)),
)
.service(
web::resource("/something_v2")
.route(web::post().to_async(create_something_v2)),
)
}) })
.resource("/something_v2", |r| { .bind("127.0.0.1:8088")?
r.method(Method::POST).with_async(create_something_v2) .run()
})
}).bind("127.0.0.1:8088")
.unwrap()
.start();
println!("Started http server: 127.0.0.1:8088");
let _ = sys.run();
} }

View File

@ -1,14 +0,0 @@
[package]
name = "cookie-auth-full"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<String>;
/// Remember identity.
fn remember(&self, identity: String);
/// This method is used to 'forget' the current identity on subsequent
/// requests.
fn forget(&self);
}
impl<S> RequestIdentity for HttpRequest<S> {
fn identity(&self) -> Option<String> {
if let Some(id) = self.extensions().get::<IdentityBox>() {
return id.0.identity().map(|s| s.to_owned());
}
None
}
fn remember(&self, identity: String) {
if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() {
return id.0.as_mut().remember(identity);
}
}
fn forget(&self) {
if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() {
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<Response>;
}
/// Identity policy definition.
pub trait IdentityPolicy<S>: Sized + 'static {
type Identity: Identity;
type Future: Future<Item = Self::Identity, Error = Error>;
/// Parse the session from request and load data from a service identity.
fn from_request(&self, request: &mut HttpRequest<S>) -> Self::Future;
}
/// Middleware that implements identity service
pub struct IdentityService<T> {
backend: T,
}
impl<T> IdentityService<T> {
/// Create new identity service with specified backend.
pub fn new(backend: T) -> Self {
IdentityService { backend }
}
}
struct IdentityBox(Box<Identity>);
#[doc(hidden)]
unsafe impl Send for IdentityBox {}
#[doc(hidden)]
unsafe impl Sync for IdentityBox {}
impl<S: 'static, T: IdentityPolicy<S>> Middleware<S> for IdentityService<T> {
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
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<S>, resp: HttpResponse) -> Result<Response> {
if let Some(mut id) = req.extensions_mut().remove::<IdentityBox>() {
id.0.write(resp)
} else {
Ok(Response::Done(resp))
}
}
}
/// Identity that uses private cookies as identity storage
pub struct CookieIdentity {
changed: bool,
identity: Option<String>,
inner: Rc<CookieIdentityInner>,
}
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<Response> {
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<String>,
secure: bool,
max_age: Option<Duration>,
}
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<String>) -> 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<S>(&self, req: &mut HttpRequest<S>) -> Option<String> {
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<CookieIdentityInner>);
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<S: Into<String>>(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<S: Into<String>>(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<S: Into<String>>(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<S> IdentityPolicy<S> for CookieIdentityPolicy {
type Identity = CookieIdentity;
type Future = FutureResult<CookieIdentity, Error>;
fn from_request(&self, req: &mut HttpRequest<S>) -> Self::Future {
let identity = self.0.load(req);
FutOk(CookieIdentity {
identity,
changed: false,
inner: Rc::clone(&self.0),
})
}
}

View File

@ -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();
}

View File

@ -5,6 +5,5 @@ authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
workspace = "../" workspace = "../"
[dependencies] [dependencies]
actix = "0.7" actix-web = { git="https://github.com/actix/actix-web.git" }
actix-web = "0.7"
env_logger = "0.5" env_logger = "0.5"

View File

@ -2,9 +2,9 @@ extern crate actix;
extern crate actix_web; extern crate actix_web;
extern crate env_logger; extern crate env_logger;
use actix_web::{middleware, server, App, HttpRequest, HttpResponse};
use actix_web::middleware::identity::RequestIdentity; use actix_web::middleware::identity::RequestIdentity;
use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService}; use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
use actix_web::{middleware, server, App, HttpRequest, HttpResponse};
fn index(req: &HttpRequest) -> String { fn index(req: &HttpRequest) -> String {
format!("Hello {}", req.identity().unwrap_or("Anonymous".to_owned())) format!("Hello {}", req.identity().unwrap_or("Anonymous".to_owned()))
@ -36,7 +36,8 @@ fn main() {
.resource("/login", |r| r.f(login)) .resource("/login", |r| r.f(login))
.resource("/logout", |r| r.f(logout)) .resource("/logout", |r| r.f(logout))
.resource("/", |r| r.f(index)) .resource("/", |r| r.f(index))
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -2,12 +2,13 @@
name = "cookie-session" name = "cookie-session"
version = "0.1.0" version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
workspace = "../" workspace = ".."
edition = "2018"
[dependencies] [dependencies]
actix = "0.7" actix-web = { git="https://github.com/actix/actix-web.git", branch = "1.0" }
actix-web = "^0.7" actix-session = { git="https://github.com/actix/actix-web.git", branch = "1.0" }
futures = "0.1" futures = "0.1"
time = "0.1" time = "0.1"
env_logger = "0.5" env_logger = "0.6"

View File

@ -5,50 +5,38 @@
//! //!
//! [User guide](https://actix.rs/book/actix-web/sec-9-middlewares.html#user-sessions) //! [User guide](https://actix.rs/book/actix-web/sec-9-middlewares.html#user-sessions)
extern crate actix; use actix_session::{CookieSession, Session};
extern crate actix_web; use actix_web::{middleware::Logger, web, App, HttpRequest, HttpServer, Result};
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;
/// simple index handler with session /// simple index handler with session
fn index(req: &HttpRequest) -> Result<&'static str> { fn index(session: Session, req: HttpRequest) -> Result<&'static str> {
println!("{:?}", req); println!("{:?}", req);
// RequestSession trait is used for session access // RequestSession trait is used for session access
let mut counter = 1; let mut counter = 1;
if let Some(count) = req.session().get::<i32>("counter")? { if let Some(count) = session.get::<i32>("counter")? {
println!("SESSION value: {}", count); println!("SESSION value: {}", count);
counter = count + 1; counter = count + 1;
req.session().set("counter", counter)?; session.set("counter", counter)?;
} else { } else {
req.session().set("counter", counter)?; session.set("counter", counter)?;
} }
Ok("welcome!") Ok("welcome!")
} }
fn main() { fn main() -> std::io::Result<()> {
env::set_var("RUST_LOG", "actix_web=info"); std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init(); env_logger::init();
let sys = actix::System::new("session-example");
server::new(|| { HttpServer::new(|| {
App::new() App::new()
// enable logger // enable logger
.middleware(middleware::Logger::default()) .middleware(Logger::default())
// cookie session middleware // cookie session middleware
.middleware(session::SessionStorage::new( .middleware(CookieSession::signed(&[0; 32]).secure(false))
session::CookieSessionBackend::signed(&[0; 32]).secure(false) .service(web::resource("/").to(index))
)) })
.resource("/", |r| r.f(index)) .bind("127.0.0.1:8080")?
}).bind("127.0.0.1:8080") .run()
.expect("Can not bind to 127.0.0.1:8080")
.start();
println!("Starting http server: 127.0.0.1:8080");
let _ = sys.run();
} }

View File

@ -2,11 +2,13 @@
name = "error_handling" name = "error_handling"
version = "0.1.0" version = "0.1.0"
authors = ["dowwie <dkcdkg@gmail.com>"] authors = ["dowwie <dkcdkg@gmail.com>"]
edition = "2018"
workspace = ".."
[dependencies] [dependencies]
actix = "0.7.3" actix-web = { git="https://github.com/actix/actix-web.git", branch = "1.0" }
actix-web = "0.7.3"
failure = "0.1.2" derive_more = "0.14.0"
futures = "0.1.23" futures = "0.1.23"
rand = "0.5.4" rand = "0.5.4"
env_logger = "0.5.12" env_logger = "0.6"

View File

@ -1,6 +1,6 @@
/* /*
The goal of this example is to show how to propagate a custom error type, derived The goal of this example is to show how to propagate a custom error type,
from the Fail trait, to a web handler that will evaluate the type of error that to a web handler that will evaluate the type of error that
was raised and return an appropriate HTTPResponse. was raised and return an appropriate HTTPResponse.
This example uses a 50/50 chance of returning 200 Ok, otherwise one of four possible This example uses a 50/50 chance of returning 200 Ok, otherwise one of four possible
@ -12,128 +12,91 @@ http errors will be chosen, each with an equal chance of being selected:
*/ */
use actix_web::{web, App, Error, HttpResponse, HttpServer, ResponseError};
extern crate actix; use derive_more::Display; // naming it clearly for illustration purposes
extern crate actix_web; use futures::future::{err, ok, Future};
extern crate env_logger; use rand::{
#[macro_use] extern crate failure; distributions::{Distribution, Standard},
extern crate futures; thread_rng, Rng,
extern crate rand;
use actix_web::{
http::Method, server, App, AsyncResponder, Error as ActixWebError,
HttpResponse, HttpRequest
}; };
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(Debug, Display)]
#[derive(Fail, Debug)]
pub enum CustomError { pub enum CustomError {
#[fail(display = "Custom Error 1")] #[display(fmt = "Custom Error 1")]
CustomOne, CustomOne,
#[fail(display = "Custom Error 2")] #[display(fmt = "Custom Error 2")]
CustomTwo, CustomTwo,
#[fail(display = "Custom Error 3")] #[display(fmt = "Custom Error 3")]
CustomThree, CustomThree,
#[fail(display = "Custom Error 4")] #[display(fmt = "Custom Error 4")]
CustomFour CustomFour,
} }
impl Distribution<CustomError> for Standard { impl Distribution<CustomError> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> CustomError { fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> CustomError {
match rng.gen_range(0, 4) { match rng.gen_range(0, 4) {
0 => CustomError::CustomOne, 0 => CustomError::CustomOne,
1 => CustomError::CustomTwo, 1 => CustomError::CustomTwo,
2 => CustomError::CustomThree, 2 => CustomError::CustomThree,
_ => CustomError::CustomFour _ => CustomError::CustomFour,
} }
} }
} }
/* /// Actix web uses `ResponseError` for conversion of errors to a response
impl ResponseError for CustomError { impl ResponseError for CustomError {
fn error_response(&self) -> HttpResponse { 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 /// randomly returns either () or one of the 4 CustomError variants
//fn do_something_random() -> impl Future<Item = Result<(), FailureError>,
// Error = ActixWebError> {
fn do_something_random() -> impl Future<Item = (), Error = CustomError> { fn do_something_random() -> impl Future<Item = (), Error = CustomError> {
let mut rng = thread_rng(); let mut rng = thread_rng();
// 20% chance that () will be returned by this function // 20% chance that () will be returned by this function
if rng.gen_bool(2.0 / 10.0) { if rng.gen_bool(2.0 / 10.0) {
return fut_ok(()) ok(())
} } else {
err(rand::random::<CustomError>())
let err: CustomError = rand::random();
return fut_err(err)
}
fn do_something(_req: HttpRequest)
-> impl Future<Item = HttpResponse, Error = ActixWebError> {
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<Item = HttpResponse, Error = Error> {
do_something_random().from_err().and_then(|_| {
HttpResponse::Ok().body("Nothing interesting happened. Try again.")
}) })
.responder()
} }
fn main() -> std::io::Result<()> {
fn main() { std::env::set_var("RUST_LOG", "actix_web=info");
::std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init(); env_logger::init();
let sys = actix::System::new("error_handling_example");
server::new(move || { HttpServer::new(move || {
App::new() App::new().service(
.resource("/something", |r| web::resource("/something").route(web::get().to_async(do_something)),
r.method(Method::GET) )
.with_async(do_something)) })
}).bind("127.0.0.1:8088") .bind("127.0.0.1:8088")?
.unwrap() .run()
.start();
println!("Started http server: 127.0.0.1:8088");
let _ = sys.run();
} }

View File

@ -18,7 +18,8 @@ fn main() {
let _addr = server::new(|| { let _addr = server::new(|| {
App::with_state(AppState { App::with_state(AppState {
foo: "bar".to_string(), foo: "bar".to_string(),
}).middleware(middleware::Logger::default()) })
.middleware(middleware::Logger::default())
.resource("/", |r| { .resource("/", |r| {
r.method(http::Method::GET).with(index); r.method(http::Method::GET).with(index);
}) })
@ -31,7 +32,8 @@ fn main() {
.resource("/post3", |r| { .resource("/post3", |r| {
r.method(http::Method::POST).with(handle_post_3) r.method(http::Method::POST).with(handle_post_3)
}) })
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.expect("Can not bind to 127.0.0.1:8080") .expect("Can not bind to 127.0.0.1:8080")
.start(); .start();

View File

@ -19,7 +19,8 @@ fn main() {
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
.resource("/index.html", |r| r.f(|_| "Hello world!")) .resource("/index.html", |r| r.f(|_| "Hello world!"))
.resource("/", |r| r.f(index)) .resource("/", |r| r.f(index))
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -87,25 +87,29 @@ fn main() {
.value_name("LISTEN ADDR") .value_name("LISTEN ADDR")
.index(1) .index(1)
.required(true), .required(true),
).arg( )
.arg(
Arg::with_name("listen_port") Arg::with_name("listen_port")
.takes_value(true) .takes_value(true)
.value_name("LISTEN PORT") .value_name("LISTEN PORT")
.index(2) .index(2)
.required(true), .required(true),
).arg( )
.arg(
Arg::with_name("forward_addr") Arg::with_name("forward_addr")
.takes_value(true) .takes_value(true)
.value_name("FWD ADDR") .value_name("FWD ADDR")
.index(3) .index(3)
.required(true), .required(true),
).arg( )
.arg(
Arg::with_name("forward_port") Arg::with_name("forward_port")
.takes_value(true) .takes_value(true)
.value_name("FWD PORT") .value_name("FWD PORT")
.index(4) .index(4)
.required(true), .required(true),
).get_matches(); )
.get_matches();
let listen_addr = matches.value_of("listen_addr").unwrap(); let listen_addr = matches.value_of("listen_addr").unwrap();
let listen_port = value_t!(matches, "listen_port", u16).unwrap_or_else(|e| e.exit()); let listen_port = value_t!(matches, "listen_port", u16).unwrap_or_else(|e| e.exit());
@ -121,13 +125,15 @@ fn main() {
.unwrap() .unwrap()
.next() .next()
.unwrap() .unwrap()
)).unwrap(); ))
.unwrap();
server::new(move || { server::new(move || {
App::with_state(AppState::init(forward_url.clone())).default_resource(|r| { App::with_state(AppState::init(forward_url.clone())).default_resource(|r| {
r.f(forward); r.f(forward);
}) })
}).workers(32) })
.workers(32)
.bind((listen_addr, listen_port)) .bind((listen_addr, listen_port))
.expect("Cannot bind listening port") .expect("Cannot bind listening port")
.system_exit() .system_exit()

View File

@ -12,15 +12,18 @@ use futures::{Future, Stream};
/// Stream client request response and then send body to a server response /// Stream client request response and then send body to a server response
fn index(_req: &HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> { fn index(_req: &HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> {
client::ClientRequest::get("http://127.0.0.1:8081/") client::ClientRequest::get("http://127.0.0.1:8081/")
.finish().unwrap() .finish()
.unwrap()
.send() .send()
.map_err(Error::from) // <- convert SendRequestError to an Error .map_err(Error::from) // <- convert SendRequestError to an Error
.and_then( .and_then(|resp| {
|resp| resp.body() // <- this is MessageBody type, resolves to complete body resp.body() // <- this is MessageBody type, resolves to complete body
.from_err() // <- convert PayloadError to an Error .from_err() // <- convert PayloadError to an Error
.and_then(|body| { // <- we got complete body, now send as server response .and_then(|body| {
// <- we got complete body, now send as server response
Ok(HttpResponse::Ok().body(body)) Ok(HttpResponse::Ok().body(body))
})) })
})
.responder() .responder()
} }
@ -28,10 +31,12 @@ fn index(_req: &HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>>
fn streaming(_req: &HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> { fn streaming(_req: &HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> {
// send client request // send client request
client::ClientRequest::get("https://www.rust-lang.org/en-US/") client::ClientRequest::get("https://www.rust-lang.org/en-US/")
.finish().unwrap() .finish()
.unwrap()
.send() // <- connect to host and send request .send() // <- connect to host and send request
.map_err(Error::from) // <- convert SendRequestError to an Error .map_err(Error::from) // <- convert SendRequestError to an Error
.and_then(|resp| { // <- we received client response .and_then(|resp| {
// <- we received client response
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
// read one chunk from client response and send this chunk to a server response // read one chunk from client response and send this chunk to a server response
// .from_err() converts PayloadError to an Error // .from_err() converts PayloadError to an Error
@ -50,7 +55,8 @@ fn main() {
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
.resource("/streaming", |r| r.f(streaming)) .resource("/streaming", |r| r.f(streaming))
.resource("/", |r| r.f(index)) .resource("/", |r| r.f(index))
}).workers(1) })
.workers(1)
.bind("127.0.0.1:8080") .bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -24,7 +24,8 @@ fn main() {
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
.resource("/index.html", |r| r.f(|_| "Hello world!")) .resource("/index.html", |r| r.f(|_| "Hello world!"))
.resource("/", |r| r.f(index)) .resource("/", |r| r.f(index))
}).workers(1) })
.workers(1)
.bind("127.0.0.1:8081") .bind("127.0.0.1:8081")
.unwrap() .unwrap()
.start(); .start();

View File

@ -93,12 +93,15 @@ fn main() {
// Start http server // Start http server
server::new(move || { server::new(move || {
App::with_state(AppState{executor: addr.clone()}) App::with_state(AppState {
executor: addr.clone(),
})
// enable logger // enable logger
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
.resource("/graphql", |r| r.method(http::Method::POST).with(graphql)) .resource("/graphql", |r| r.method(http::Method::POST).with(graphql))
.resource("/graphiql", |r| r.method(http::Method::GET).h(graphiql)) .resource("/graphiql", |r| r.method(http::Method::GET).h(graphiql))
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -16,12 +16,15 @@ fn main() {
.middleware(simple::SayHi) .middleware(simple::SayHi)
// .middleware(redirect::CheckLogin) // .middleware(redirect::CheckLogin)
.resource("/login", |r| { .resource("/login", |r| {
r.f(|_| "You are on /login. Go to src/redirect.rs to change this behavior.") r.f(|_| {
"You are on /login. Go to src/redirect.rs to change this behavior."
})
}) })
.resource("/", |r| { .resource("/", |r| {
r.f(|_| "Hello, middleware! Check the console where the server is run.") r.f(|_| "Hello, middleware! Check the console where the server is run.")
}) })
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -101,12 +101,14 @@ fn main() {
server::new(|| { server::new(|| {
App::with_state(AppState { App::with_state(AppState {
counter: Cell::new(0), counter: Cell::new(0),
}).middleware(middleware::Logger::default()) })
.middleware(middleware::Logger::default())
.resource("/", |r| { .resource("/", |r| {
r.method(http::Method::GET).with(index); r.method(http::Method::GET).with(index);
r.method(http::Method::POST).with(upload); r.method(http::Method::POST).with(upload);
}) })
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -45,7 +45,8 @@ fn main() {
App::new() App::new()
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
.resource("/", |r| r.method(http::Method::POST).f(index)) .resource("/", |r| r.method(http::Method::POST).f(index))
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.shutdown_timeout(1) .shutdown_timeout(1)
.start(); .start();

View File

@ -35,12 +35,14 @@ fn main() {
// enable logger // enable logger
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
// redis session middleware // redis session middleware
.middleware(SessionStorage::new( .middleware(SessionStorage::new(RedisSessionBackend::new(
RedisSessionBackend::new("127.0.0.1:6379", &[0; 32]) "127.0.0.1:6379",
)) &[0; 32],
)))
// register simple route, handle all methods // register simple route, handle all methods
.resource("/", |r| r.f(index)) .resource("/", |r| r.f(index))
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -49,13 +49,16 @@ fn main() {
// register simple handler, handle all methods // register simple handler, handle all methods
.resource("/index.html", |r| r.f(index)) .resource("/index.html", |r| r.f(index))
// with path parameters // with path parameters
.resource("/", |r| r.method(http::Method::GET).f(|_| { .resource("/", |r| {
r.method(http::Method::GET).f(|_| {
HttpResponse::Found() HttpResponse::Found()
.header("LOCATION", "/index.html") .header("LOCATION", "/index.html")
.finish() .finish()
})) })
})
.handler("/static", StaticFiles::new("static").unwrap()) .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() .unwrap()
.start(); .start();

View File

@ -14,8 +14,10 @@ pub struct AppState {
/// creates and returns the app after mounting all routes/resources /// creates and returns the app after mounting all routes/resources
pub fn create_app(db: Addr<DbExecutor>) -> App<AppState> { pub fn create_app(db: Addr<DbExecutor>) -> App<AppState> {
// secret is a random minimum 32 bytes long base 64 string // 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 secret: String =
let domain: String = std::env::var("DOMAIN").unwrap_or_else(|_| "localhost".to_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 }) App::with_state(AppState { db })
.middleware(Logger::default()) .middleware(Logger::default())

View File

@ -1,9 +1,9 @@
use actix::{Handler, Message}; use actix::{Handler, Message};
use actix_web::{middleware::identity::RequestIdentity, FromRequest, HttpRequest};
use bcrypt::verify;
use diesel::prelude::*; use diesel::prelude::*;
use errors::ServiceError; use errors::ServiceError;
use models::{DbExecutor, User, SlimUser}; use models::{DbExecutor, SlimUser, User};
use bcrypt::verify;
use actix_web::{FromRequest, HttpRequest, middleware::identity::RequestIdentity};
use utils::decode_token; use utils::decode_token;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -19,22 +19,24 @@ impl Message for AuthData {
impl Handler<AuthData> for DbExecutor { impl Handler<AuthData> for DbExecutor {
type Result = Result<SlimUser, ServiceError>; type Result = Result<SlimUser, ServiceError>;
fn handle(&mut self, msg: AuthData, _: &mut Self::Context) -> Self::Result { fn handle(&mut self, msg: AuthData, _: &mut Self::Context) -> Self::Result {
use schema::users::dsl::{users, email}; use schema::users::dsl::{email, users};
let conn: &PgConnection = &self.0.get().unwrap(); let conn: &PgConnection = &self.0.get().unwrap();
let mut items = users let mut items = users.filter(email.eq(&msg.email)).load::<User>(conn)?;
.filter(email.eq(&msg.email))
.load::<User>(conn)?;
if let Some(user) = items.pop() { if let Some(user) = items.pop() {
match verify(&msg.password, &user.password) { match verify(&msg.password, &user.password) {
Ok(matching) => if matching { Ok(matching) => {
if matching {
return Ok(user.into()); return Ok(user.into());
}, }
}
Err(_) => (), Err(_) => (),
} }
} }
Err(ServiceError::BadRequest("Username and Password don't match".into())) Err(ServiceError::BadRequest(
"Username and Password don't match".into(),
))
} }
} }

View File

@ -1,13 +1,16 @@
use actix_web::{AsyncResponder, FutureResponse, HttpResponse, HttpRequest, ResponseError, Json};
use actix_web::middleware::identity::RequestIdentity; use actix_web::middleware::identity::RequestIdentity;
use actix_web::{
AsyncResponder, FutureResponse, HttpRequest, HttpResponse, Json, ResponseError,
};
use futures::future::Future; use futures::future::Future;
use utils::create_token; use utils::create_token;
use app::AppState; use app::AppState;
use auth_handler::{AuthData, LoggedUser}; use auth_handler::{AuthData, LoggedUser};
pub fn login((auth_data, req): (Json<AuthData>, HttpRequest<AppState>)) pub fn login(
-> FutureResponse<HttpResponse> { (auth_data, req): (Json<AuthData>, HttpRequest<AppState>),
) -> FutureResponse<HttpResponse> {
req.state() req.state()
.db .db
.send(auth_data.into_inner()) .send(auth_data.into_inner())
@ -19,7 +22,8 @@ pub fn login((auth_data, req): (Json<AuthData>, HttpRequest<AppState>))
Ok(HttpResponse::Ok().into()) Ok(HttpResponse::Ok().into())
} }
Err(err) => Ok(err.error_response()), Err(err) => Ok(err.error_response()),
}).responder() })
.responder()
} }
pub fn logout(req: HttpRequest<AppState>) -> HttpResponse { pub fn logout(req: HttpRequest<AppState>) -> HttpResponse {

View File

@ -9,8 +9,8 @@ fn get_api_key() -> String {
pub fn send_invitation(invitation: &Invitation) { pub fn send_invitation(invitation: &Invitation) {
let tm = Transmission::new_eu(get_api_key()); let tm = Transmission::new_eu(get_api_key());
let sending_email = let sending_email = std::env::var("SENDING_EMAIL_ADDRESS")
std::env::var("SENDING_EMAIL_ADDRESS").expect("SENDING_EMAIL_ADDRESS must be set"); .expect("SENDING_EMAIL_ADDRESS must be set");
// new email message with sender name and email // new email message with sender name and email
let mut email = Message::new(EmailAddress::new(sending_email, "Let's Organise")); let mut email = Message::new(EmailAddress::new(sending_email, "Let's Organise"));
@ -39,7 +39,6 @@ pub fn send_invitation(invitation: &Invitation) {
.to_string() .to_string()
); );
// complete the email message with details // complete the email message with details
email email
.add_recipient(recipient) .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 // Note that we only print out the error response from email api
match result { match result {
Ok(res) => { Ok(res) => match res {
match res {
TransmissionResponse::ApiResponse(api_res) => { TransmissionResponse::ApiResponse(api_res) => {
println!("API Response: \n {:#?}", api_res); println!("API Response: \n {:#?}", api_res);
} }
TransmissionResponse::ApiError(errors) => { TransmissionResponse::ApiError(errors) => {
println!("Response Errors: \n {:#?}", &errors); println!("Response Errors: \n {:#?}", &errors);
} }
} },
}
Err(error) => { Err(error) => {
println!("error \n {:#?}", error); println!("error \n {:#?}", error);
} }

View File

@ -1,9 +1,8 @@
use actix_web::{error::ResponseError, HttpResponse}; use actix_web::{error::ResponseError, HttpResponse};
use std::convert::From;
use diesel::result::{DatabaseErrorKind, Error}; use diesel::result::{DatabaseErrorKind, Error};
use std::convert::From;
use uuid::ParseError; use uuid::ParseError;
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
pub enum ServiceError { pub enum ServiceError {
#[fail(display = "Internal Server Error")] #[fail(display = "Internal Server Error")]
@ -20,9 +19,14 @@ pub enum ServiceError {
impl ResponseError for ServiceError { impl ResponseError for ServiceError {
fn error_response(&self) -> HttpResponse { fn error_response(&self) -> HttpResponse {
match *self { match *self {
ServiceError::InternalServerError => HttpResponse::InternalServerError().json("Internal Server Error, Please try later"), ServiceError::InternalServerError => HttpResponse::InternalServerError()
ServiceError::BadRequest(ref message) => HttpResponse::BadRequest().json(message), .json("Internal Server Error, Please try later"),
ServiceError::Unauthorized => HttpResponse::Unauthorized().json("Unauthorized") ServiceError::BadRequest(ref message) => {
HttpResponse::BadRequest().json(message)
}
ServiceError::Unauthorized => {
HttpResponse::Unauthorized().json("Unauthorized")
}
} }
} }
} }
@ -42,12 +46,13 @@ impl From<Error> for ServiceError {
match error { match error {
Error::DatabaseError(kind, info) => { Error::DatabaseError(kind, info) => {
if let DatabaseErrorKind::UniqueViolation = kind { 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); return ServiceError::BadRequest(message);
} }
ServiceError::InternalServerError ServiceError::InternalServerError
} }
_ => ServiceError::InternalServerError _ => ServiceError::InternalServerError,
} }
} }
} }

View File

@ -35,5 +35,3 @@ impl Handler<CreateInvitation> for DbExecutor {
Ok(inserted_invitation) Ok(inserted_invitation)
} }
} }

View File

@ -1,4 +1,6 @@
use actix_web::{AsyncResponder, FutureResponse, HttpResponse, Json, ResponseError, State}; use actix_web::{
AsyncResponder, FutureResponse, HttpResponse, Json, ResponseError, State,
};
use futures::future::Future; use futures::future::Future;
use app::AppState; use app::AppState;
@ -18,5 +20,6 @@ pub fn register_email(
Ok(HttpResponse::Ok().into()) Ok(HttpResponse::Ok().into())
} }
Err(err) => Ok(err.error_response()), Err(err) => Ok(err.error_response()),
}).responder() })
.responder()
} }

View File

@ -1,18 +1,18 @@
// to avoid the warning from diesel macros // to avoid the warning from diesel macros
#![allow(proc_macro_derive_resolution_fallback)] #![allow(proc_macro_derive_resolution_fallback)]
extern crate bcrypt;
extern crate actix; extern crate actix;
extern crate actix_web; extern crate actix_web;
extern crate env_logger; extern crate bcrypt;
extern crate serde;
extern crate chrono; extern crate chrono;
extern crate dotenv; extern crate dotenv;
extern crate env_logger;
extern crate futures; extern crate futures;
extern crate r2d2;
extern crate uuid;
extern crate jsonwebtoken as jwt; extern crate jsonwebtoken as jwt;
extern crate r2d2;
extern crate serde;
extern crate sparkpost; extern crate sparkpost;
extern crate uuid;
#[macro_use] #[macro_use]
extern crate diesel; extern crate diesel;
#[macro_use] #[macro_use]
@ -21,26 +21,25 @@ extern crate serde_derive;
extern crate failure; extern crate failure;
mod app; mod app;
mod models;
mod schema;
mod errors;
mod auth_handler; mod auth_handler;
mod auth_routes; mod auth_routes;
mod email_service;
mod errors;
mod invitation_handler; mod invitation_handler;
mod invitation_routes; mod invitation_routes;
mod models;
mod register_handler; mod register_handler;
mod register_routes; mod register_routes;
mod schema;
mod utils; mod utils;
mod email_service;
use models::DbExecutor;
use actix::prelude::*; use actix::prelude::*;
use actix_web::server; use actix_web::server;
use diesel::{r2d2::ConnectionManager, PgConnection}; use diesel::{r2d2::ConnectionManager, PgConnection};
use dotenv::dotenv; use dotenv::dotenv;
use models::DbExecutor;
use std::env; use std::env;
fn main() { fn main() {
dotenv().ok(); dotenv().ok();
std::env::set_var("RUST_LOG", "simple-auth-server=debug,actix_web=info"); std::env::set_var("RUST_LOG", "simple-auth-server=debug,actix_web=info");
@ -55,7 +54,8 @@ fn main() {
.build(manager) .build(manager)
.expect("Failed to create pool."); .expect("Failed to create pool.");
let address: Addr<DbExecutor> = SyncArbiter::start(4, move || DbExecutor(pool.clone())); let address: Addr<DbExecutor> =
SyncArbiter::start(4, move || DbExecutor(pool.clone()));
server::new(move || app::create_app(address.clone())) server::new(move || app::create_app(address.clone()))
.bind("127.0.0.1:3000") .bind("127.0.0.1:3000")

View File

@ -1,11 +1,11 @@
use actix::{Actor, SyncContext}; use actix::{Actor, SyncContext};
use chrono::{Local, NaiveDateTime};
use diesel::pg::PgConnection; use diesel::pg::PgConnection;
use diesel::r2d2::{ConnectionManager, Pool}; use diesel::r2d2::{ConnectionManager, Pool};
use chrono::{NaiveDateTime, Local};
use uuid::Uuid;
use std::convert::From; use std::convert::From;
use uuid::Uuid;
use schema::{users, invitations}; use schema::{invitations, users};
/// This is db executor actor. can be run in parallel /// This is db executor actor. can be run in parallel
pub struct DbExecutor(pub Pool<ConnectionManager<PgConnection>>); pub struct DbExecutor(pub Pool<ConnectionManager<PgConnection>>);
@ -51,8 +51,6 @@ pub struct SlimUser {
impl From<User> for SlimUser { impl From<User> for SlimUser {
fn from(user: User) -> Self { fn from(user: User) -> Self {
SlimUser { SlimUser { email: user.email }
email: user.email
}
} }
} }

View File

@ -2,9 +2,9 @@ use actix::{Handler, Message};
use chrono::Local; use chrono::Local;
use diesel::prelude::*; use diesel::prelude::*;
use errors::ServiceError; use errors::ServiceError;
use models::{DbExecutor, Invitation, User, SlimUser}; use models::{DbExecutor, Invitation, SlimUser, User};
use uuid::Uuid;
use utils::hash_password; use utils::hash_password;
use uuid::Uuid;
// UserData is used to extract data from a post request by the client // UserData is used to extract data from a post request by the client
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -23,11 +23,10 @@ impl Message for RegisterUser {
type Result = Result<SlimUser, ServiceError>; type Result = Result<SlimUser, ServiceError>;
} }
impl Handler<RegisterUser> for DbExecutor { impl Handler<RegisterUser> for DbExecutor {
type Result = Result<SlimUser, ServiceError>; type Result = Result<SlimUser, ServiceError>;
fn handle(&mut self, msg: RegisterUser, _: &mut Self::Context) -> Self::Result { fn handle(&mut self, msg: RegisterUser, _: &mut Self::Context) -> Self::Result {
use schema::invitations::dsl::{invitations, id}; use schema::invitations::dsl::{id, invitations};
use schema::users::dsl::users; use schema::users::dsl::users;
let conn: &PgConnection = &self.0.get().unwrap(); let conn: &PgConnection = &self.0.get().unwrap();
@ -35,7 +34,8 @@ impl Handler<RegisterUser> for DbExecutor {
// return early with error that will be converted to ServiceError // return early with error that will be converted to ServiceError
let invitation_id = Uuid::parse_str(&msg.invitation_id)?; let invitation_id = Uuid::parse_str(&msg.invitation_id)?;
invitations.filter(id.eq(invitation_id)) invitations
.filter(id.eq(invitation_id))
.load::<Invitation>(conn) .load::<Invitation>(conn)
.map_err(|_db_error| ServiceError::BadRequest("Invalid Invitation".into())) .map_err(|_db_error| ServiceError::BadRequest("Invalid Invitation".into()))
.and_then(|mut result| { .and_then(|mut result| {
@ -45,9 +45,8 @@ impl Handler<RegisterUser> for DbExecutor {
// try hashing the password, else return the error that will be converted to ServiceError // try hashing the password, else return the error that will be converted to ServiceError
let password: String = hash_password(&msg.password)?; let password: String = hash_password(&msg.password)?;
let user = User::with_details(invitation.email, password); let user = User::with_details(invitation.email, password);
let inserted_user: User = diesel::insert_into(users) let inserted_user: User =
.values(&user) diesel::insert_into(users).values(&user).get_result(conn)?;
.get_result(conn)?;
return Ok(inserted_user.into()); return Ok(inserted_user.into());
} }
@ -56,5 +55,3 @@ impl Handler<RegisterUser> for DbExecutor {
}) })
} }
} }

View File

@ -1,22 +1,27 @@
use actix_web::{AsyncResponder, FutureResponse, HttpResponse, ResponseError, State, Json, Path}; use actix_web::{
AsyncResponder, FutureResponse, HttpResponse, Json, Path, ResponseError, State,
};
use futures::future::Future; use futures::future::Future;
use app::AppState; use app::AppState;
use register_handler::{RegisterUser, UserData}; use register_handler::{RegisterUser, UserData};
pub fn register_user(
pub fn register_user((invitation_id, user_data, state): (Path<String>, Json<UserData>, State<AppState>)) (invitation_id, user_data, state): (Path<String>, Json<UserData>, State<AppState>),
-> FutureResponse<HttpResponse> { ) -> FutureResponse<HttpResponse> {
let msg = RegisterUser { let msg = RegisterUser {
// into_inner() returns the inner string value from Path // into_inner() returns the inner string value from Path
invitation_id: invitation_id.into_inner(), invitation_id: invitation_id.into_inner(),
password: user_data.password.clone(), password: user_data.password.clone(),
}; };
state.db.send(msg) state
.db
.send(msg)
.from_err() .from_err()
.and_then(|db_response| match db_response { .and_then(|db_response| match db_response {
Ok(slim_user) => Ok(HttpResponse::Ok().json(slim_user)), Ok(slim_user) => Ok(HttpResponse::Ok().json(slim_user)),
Err(service_error) => Ok(service_error.error_response()), Err(service_error) => Ok(service_error.error_response()),
}).responder() })
.responder()
} }

View File

@ -14,7 +14,4 @@ table! {
} }
} }
allow_tables_to_appear_in_same_query!( allow_tables_to_appear_in_same_query!(invitations, users,);
invitations,
users,
);

View File

@ -17,9 +17,12 @@ fn main() {
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
.handler( .handler(
"/", "/",
fs::StaticFiles::new("./static/").unwrap().index_file("index.html") fs::StaticFiles::new("./static/")
.unwrap()
.index_file("index.html"),
) )
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.expect("Can not start server on given IP/Port") .expect("Can not start server on given IP/Port")
.start(); .start();

View File

@ -24,7 +24,8 @@ fn index(query: Query<HashMap<String, String>>) -> Result<HttpResponse> {
UserTemplate { UserTemplate {
name: name, name: name,
text: "Welcome!", text: "Welcome!",
}.render() }
.render()
.unwrap() .unwrap()
} else { } else {
Index.render().unwrap() Index.render().unwrap()
@ -38,7 +39,8 @@ fn main() {
// start http server // start http server
server::new(move || { server::new(move || {
App::new().resource("/", |r| r.method(http::Method::GET).with(index)) App::new().resource("/", |r| r.method(http::Method::GET).with(index))
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -48,7 +48,8 @@ fn main() {
// enable logger // enable logger
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
.resource("/", |r| r.method(http::Method::GET).with(index)) .resource("/", |r| r.method(http::Method::GET).with(index))
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -23,7 +23,8 @@ fn main() {
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
.resource("/index.html", |r| r.f(|_| "Hello world!")) .resource("/index.html", |r| r.f(|_| "Hello world!"))
.resource("/", |r| r.f(index)) .resource("/", |r| r.f(index))
}).start_incoming(listener.incoming(), false); })
.start_incoming(listener.incoming(), false);
println!("Started http server: /tmp/actix-uds.socket"); println!("Started http server: /tmp/actix-uds.socket");
let _ = sys.run(); let _ = sys.run();

View File

@ -39,7 +39,8 @@ fn main() {
}) })
.register() .register()
}) })
}).bind("127.0.0.1:8000") })
.bind("127.0.0.1:8000")
.unwrap() .unwrap()
.shutdown_timeout(2) .shutdown_timeout(2)
.start(); .start();

View File

@ -52,7 +52,8 @@ impl WsChatSession {
} }
fut::ok(()) fut::ok(())
}).spawn(ctx); })
.spawn(ctx);
} }
fn list_rooms(&mut self, ctx: &mut ws::WebsocketContext<Self>) { fn list_rooms(&mut self, ctx: &mut ws::WebsocketContext<Self>) {
@ -66,7 +67,8 @@ impl WsChatSession {
} }
} }
fut::ok(()) fut::ok(())
}).spawn(ctx); })
.spawn(ctx);
} }
fn send_msg(&self, msg: &str) { fn send_msg(&self, msg: &str) {
@ -158,7 +160,8 @@ fn main() {
.unwrap() .unwrap()
.index_file("index.html"), .index_file("index.html"),
) )
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -12,7 +12,7 @@ extern crate tokio_io;
extern crate actix; extern crate actix;
extern crate actix_web; extern crate actix_web;
use std::time::{Instant, Duration}; use std::time::{Duration, Instant};
use actix::*; use actix::*;
use actix_web::server::HttpServer; use actix_web::server::HttpServer;
@ -185,7 +185,7 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WsChatSession {
ws::Message::Binary(bin) => println!("Unexpected binary"), ws::Message::Binary(bin) => println!("Unexpected binary"),
ws::Message::Close(_) => { ws::Message::Close(_) => {
ctx.stop(); ctx.stop();
}, }
} }
} }
} }
@ -202,9 +202,7 @@ impl WsChatSession {
println!("Websocket Client heartbeat failed, disconnecting!"); println!("Websocket Client heartbeat failed, disconnecting!");
// notify chat server // notify chat server
ctx.state() ctx.state().addr.do_send(server::Disconnect { id: act.id });
.addr
.do_send(server::Disconnect { id: act.id });
// stop actor // stop actor
ctx.stop(); ctx.stop();
@ -234,16 +232,19 @@ fn main() {
App::with_state(state) App::with_state(state)
// redirect to websocket.html // redirect to websocket.html
.resource("/", |r| r.method(http::Method::GET).f(|_| { .resource("/", |r| {
r.method(http::Method::GET).f(|_| {
HttpResponse::Found() HttpResponse::Found()
.header("LOCATION", "/static/websocket.html") .header("LOCATION", "/static/websocket.html")
.finish() .finish()
})) })
})
// websocket // websocket
.resource("/ws/", |r| r.route().f(chat_route)) .resource("/ws/", |r| r.route().f(chat_route))
// static resources // static resources
.handler("/static/", fs::StaticFiles::new("static/").unwrap()) .handler("/static/", fs::StaticFiles::new("static/").unwrap())
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -65,7 +65,9 @@ impl Encoder for ChatCodec {
type Error = io::Error; type Error = io::Error;
fn encode( fn encode(
&mut self, msg: ChatResponse, dst: &mut BytesMut, &mut self,
msg: ChatResponse,
dst: &mut BytesMut,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
let msg = json::to_string(&msg).unwrap(); let msg = json::to_string(&msg).unwrap();
let msg_ref: &[u8] = msg.as_ref(); let msg_ref: &[u8] = msg.as_ref();
@ -108,7 +110,9 @@ impl Encoder for ClientChatCodec {
type Error = io::Error; type Error = io::Error;
fn encode( fn encode(
&mut self, msg: ChatRequest, dst: &mut BytesMut, &mut self,
msg: ChatRequest,
dst: &mut BytesMut,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
let msg = json::to_string(&msg).unwrap(); let msg = json::to_string(&msg).unwrap();
let msg_ref: &[u8] = msg.as_ref(); let msg_ref: &[u8] = msg.as_ref();

View File

@ -19,7 +19,7 @@ extern crate actix_web;
use actix::*; use actix::*;
use actix_web::server::HttpServer; use actix_web::server::HttpServer;
use actix_web::{fs, http, ws, App, Error, HttpRequest, HttpResponse}; use actix_web::{fs, http, ws, App, Error, HttpRequest, HttpResponse};
use std::time::{Instant, Duration}; use std::time::{Duration, Instant};
mod codec; mod codec;
mod server; mod server;
@ -191,7 +191,7 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WsChatSession {
ws::Message::Binary(bin) => println!("Unexpected binary"), ws::Message::Binary(bin) => println!("Unexpected binary"),
ws::Message::Close(_) => { ws::Message::Close(_) => {
ctx.stop(); ctx.stop();
}, }
} }
} }
} }
@ -208,9 +208,7 @@ impl WsChatSession {
println!("Websocket Client heartbeat failed, disconnecting!"); println!("Websocket Client heartbeat failed, disconnecting!");
// notify chat server // notify chat server
ctx.state() ctx.state().addr.do_send(server::Disconnect { id: act.id });
.addr
.do_send(server::Disconnect { id: act.id });
// stop actor // stop actor
ctx.stop(); ctx.stop();
@ -247,16 +245,19 @@ fn main() {
App::with_state(state) App::with_state(state)
// redirect to websocket.html // redirect to websocket.html
.resource("/", |r| r.method(http::Method::GET).f(|_| { .resource("/", |r| {
r.method(http::Method::GET).f(|_| {
HttpResponse::Found() HttpResponse::Found()
.header("LOCATION", "/static/websocket.html") .header("LOCATION", "/static/websocket.html")
.finish() .finish()
})) })
})
// websocket // websocket
.resource("/ws/", |r| r.route().f(chat_route)) .resource("/ws/", |r| r.route().f(chat_route))
// static resources // static resources
.handler("/static/", fs::StaticFiles::new("static/").unwrap()) .handler("/static/", fs::StaticFiles::new("static/").unwrap())
}).bind("127.0.0.1:8080") })
.bind("127.0.0.1:8080")
.unwrap() .unwrap()
.start(); .start();

View File

@ -8,7 +8,7 @@ extern crate actix;
extern crate actix_web; extern crate actix_web;
extern crate env_logger; extern crate env_logger;
use std::time::{Instant, Duration}; use std::time::{Duration, Instant};
use actix::prelude::*; use actix::prelude::*;
use actix_web::{ use actix_web::{
@ -96,18 +96,23 @@ fn main() {
env_logger::init(); env_logger::init();
let sys = actix::System::new("ws-example"); let sys = actix::System::new("ws-example");
server::new( server::new(|| {
|| App::new() App::new()
// enable logger // enable logger
.middleware(middleware::Logger::default()) .middleware(middleware::Logger::default())
// websocket route // websocket route
.resource("/ws/", |r| r.method(http::Method::GET).f(ws_index)) .resource("/ws/", |r| r.method(http::Method::GET).f(ws_index))
// static files // static files
.handler("/", fs::StaticFiles::new("static/") .handler(
"/",
fs::StaticFiles::new("static/")
.unwrap() .unwrap()
.index_file("index.html"))) .index_file("index.html"),
)
})
// start http server on 127.0.0.1:8080 // start http server on 127.0.0.1:8080
.bind("127.0.0.1:8080").unwrap() .bind("127.0.0.1:8080")
.unwrap()
.start(); .start();
println!("Started http server: 127.0.0.1:8080"); println!("Started http server: 127.0.0.1:8080");