1
0
mirror of https://github.com/actix/examples synced 2025-01-22 14:05:55 +01:00

Merge branch '1.0' into 0.7

This commit is contained in:
Nikolay Kim 2019-03-29 16:43:37 -07:00
commit 9c8389e06e
105 changed files with 1801 additions and 2585 deletions

View File

@ -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 ..

View File

@ -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",

View File

@ -2,13 +2,15 @@
name = "actix_redis"
version = "0.1.0"
authors = ["dowwie <dkcdkg@gmail.com>"]
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"

View File

@ -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<CacheInfo>, HttpRequest<AppState>))
-> impl Future<Item=HttpResponse, Error=AWError> {
fn cache_stuff(
info: web::Json<CacheInfo>,
redis: web::Data<Addr<RedisActor>>,
) -> impl Future<Item = HttpResponse, Error = AWError> {
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<CacheInfo>, HttpRequest<AppState>))
let info_set = join_all(vec![one, two, three].into_iter());
info_set
.map_err(AWError::from)
.and_then(|res: Vec<Result<RespValue, ARError>>|
// 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<Result<RespValue, ARError>>|
// 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<AppState>)
-> impl Future<Item=HttpResponse, Error=AWError> {
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<RespValue, ARError>|
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<Addr<RedisActor>>,
) -> impl Future<Item = HttpResponse, Error = AWError> {
redis
.send(Command(resp_array![
"DEL",
"mydomain:one",
"mydomain:two",
"mydomain:three"
]))
.map_err(AWError::from)
.and_then(|res: Result<RespValue, ARError>| 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<Addr<RedisActor>>
}
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()
}

View File

@ -2,10 +2,13 @@
authors = ["Dan Munckton <dangit@munckfish.net>"]
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"

View File

@ -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<DbExecutor>,
}
pub fn index(req: HttpRequest<AppState>) -> FutureResponse<HttpResponse> {
req.state()
.db
.send(AllTasks)
pub fn index(
pool: web::Data<db::PgPool>,
tmpl: web::Data<Tera>,
session: Session,
) -> impl Future<Item = HttpResponse, Error = Error> {
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<AppState>) -> FutureResponse<HttpResponse> {
}
Err(e) => Err(e),
})
.responder()
}
#[derive(Deserialize)]
@ -52,34 +44,34 @@ pub struct CreateForm {
}
pub fn create(
(req, params): (HttpRequest<AppState>, Form<CreateForm>),
) -> FutureResponse<HttpResponse> {
params: web::Form<CreateForm>,
pool: web::Data<db::PgPool>,
session: Session,
) -> impl Future<Item = HttpResponse, Error = Error> {
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<AppState>, Path<UpdateParams>, Form<UpdateForm>),
) -> FutureResponse<HttpResponse> {
db: web::Data<db::PgPool>,
params: web::Path<UpdateParams>,
form: web::Form<UpdateForm>,
session: Session,
) -> impl Future<Item = HttpResponse, Error = Error> {
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<AppState>,
params: Path<UpdateParams>,
) -> FutureResponse<HttpResponse> {
req.state()
.db
.send(ToggleTask { id: params.id })
pool: web::Data<db::PgPool>,
params: web::Path<UpdateParams>,
) -> impl Future<Item = HttpResponse, Error = Error> {
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<AppState>,
params: Path<UpdateParams>,
) -> FutureResponse<HttpResponse> {
req.state()
.db
.send(DeleteTask { id: params.id })
pool: web::Data<db::PgPool>,
params: web::Path<UpdateParams>,
session: Session,
) -> impl Future<Item = HttpResponse, Error = Error> {
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<S: 'static>(
req: &HttpRequest<S>,
resp: HttpResponse,
) -> Result<Response> {
pub fn bad_request<B>(res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
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<S: 'static>(
req: &HttpRequest<S>,
resp: HttpResponse,
) -> Result<Response> {
pub fn not_found<B>(res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
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<S: 'static>(
req: &HttpRequest<S>,
resp: HttpResponse,
) -> Result<Response> {
pub fn internal_server_error<B>(
res: dev::ServiceResponse<B>,
) -> Result<ErrorHandlerResponse<B>> {
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()),
))
}

View File

@ -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<ConnectionManager<PgConnection>>;
pub type PgPool = Pool<ConnectionManager<PgConnection>>;
type PgPooledConnection = PooledConnection<ConnectionManager<PgConnection>>;
pub fn init_pool(database_url: &str) -> Result<PgPool, PoolError> {
@ -15,86 +13,29 @@ pub fn init_pool(database_url: &str) -> Result<PgPool, PoolError> {
Pool::builder().build(manager)
}
pub struct DbExecutor(pub PgPool);
impl DbExecutor {
pub fn get_conn(&self) -> Result<PgPooledConnection, Error> {
self.0.get().map_err(|e| error::ErrorInternalServerError(e))
}
fn get_conn(pool: &PgPool) -> Result<PgPooledConnection, &'static str> {
pool.get().map_err(|_| "can get connection")
}
impl Actor for DbExecutor {
type Context = SyncContext<Self>;
pub fn get_all_tasks(pool: &PgPool) -> Result<Vec<Task>, &'static str> {
Task::all(get_conn(pool)?.deref()).map_err(|_| "Error inserting task")
}
pub struct AllTasks;
impl Message for AllTasks {
type Result = Result<Vec<Task>, 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<AllTasks> for DbExecutor {
type Result = Result<Vec<Task>, 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<CreateTask> 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<ToggleTask> 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<DeleteTask> 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")
}

View File

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

View File

@ -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)]

View File

@ -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<T>(request: &HttpRequest<T>, 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<T>(req: &HttpRequest<T>) -> Result<Option<FlashMessage>> {
req.session().get::<FlashMessage>(FLASH_KEY)
pub fn get_flash(session: &Session) -> Result<Option<FlashMessage>> {
session.get::<FlashMessage>(FLASH_KEY)
}
pub fn clear_flash<T>(req: &HttpRequest<T>) {
req.session().remove(FLASH_KEY);
pub fn clear_flash(session: &Session) {
session.remove(FLASH_KEY);
}
#[derive(Deserialize, Serialize)]

View File

@ -2,18 +2,21 @@
name = "async_db"
version = "0.1.0"
authors = ["Darin Gordon <dkcdkg@gmail.com>"]
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"

View File

@ -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<r2d2_sqlite::SqliteConnectionManager>;
pub type Connection = r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager>;
pub struct DbExecutor(pub Pool);
impl Actor for DbExecutor {
type Context = SyncContext<Self>;
}
#[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<Vec<WeatherAgg>, Error>;
}
impl Handler<Queries> for DbExecutor {
type Result = Result<Vec<WeatherAgg>, 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<Item = Vec<WeatherAgg>, 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<Vec<WeatherAgg>, 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<Vec<WeatherAgg>, 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<Vec<WeatherAgg>, Error> {
fn get_coldest_years(conn: Connection) -> Result<Vec<WeatherAgg>, 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<Vec<WeatherAgg>, 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<Vec<WeatherAgg>, Error> {
fn get_hottest_months(conn: Connection) -> Result<Vec<WeatherAgg>, 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<Vec<WeatherAgg>, 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<Vec<WeatherAgg>, Error> {
fn get_coldest_months(conn: Connection) -> Result<Vec<WeatherAgg>, 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<Vec<WeatherAgg>, 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),

View File

@ -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<DbExecutor>,
}
use db::{Pool, Queries, WeatherAgg};
/// Version 1: Calls 4 queries in sequential order, as an asynchronous handler
fn asyncio_weather(state: State<AppState>) -> FutureResponse<HttpResponse> {
fn asyncio_weather(
db: web::Data<Pool>,
) -> impl Future<Item = HttpResponse, Error = AWError> {
let mut result: Vec<Vec<WeatherAgg>> = 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<AppState>) -> FutureResponse<HttpResponse> {
fn parallel_weather(
db: web::Data<Pool>,
) -> impl Future<Item = HttpResponse, Error = AWError> {
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<Option<Vec<WeatherAgg>>> =
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()
}

View File

@ -2,10 +2,12 @@
name = "awc_examples"
version = "0.1.0"
authors = ["dowwie <dkcdkg@gmail.com>"]
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"

View File

@ -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<Future<Item = SomeData, Error = Error>> {
fn step_x_v1(
data: SomeData,
client: &Client,
) -> Box<Future<Item = SomeData, Error = Error>> {
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<SomeData>,
some_data: web::Json<SomeData>,
client: web::Data<Client>,
) -> Box<Future<Item = HttpResponse, Error = Error>> {
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<Item = SomeData, Error = Error> {
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<Item = SomeData, Error = Error> {
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<SomeData>,
some_data: web::Json<SomeData>,
client: web::Data<Client>,
) -> impl Future<Item = HttpResponse, Error = Error> {
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()
}

View File

@ -1,13 +1,16 @@
[package]
name = "basics"
version = "0.1.0"
version = "1.0.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<fs::NamedFile> {
#[get("/favicon")]
fn favicon() -> Result<fs::NamedFile> {
Ok(fs::NamedFile::open("static/favicon.ico")?)
}
/// simple index handler
fn welcome(req: &HttpRequest) -> Result<HttpResponse> {
#[get("/welcome")]
fn welcome(session: Session, req: HttpRequest) -> Result<HttpResponse> {
println!("{:?}", req);
// session
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);
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<HttpResponse> {
}
/// 404 handler
fn p404(req: &HttpRequest) -> Result<fs::NamedFile> {
fn p404() -> Result<fs::NamedFile> {
Ok(fs::NamedFile::open("static/404.html")?.set_status_code(StatusCode::NOT_FOUND))
}
/// async handler
fn index_async(req: &HttpRequest) -> FutureResult<HttpResponse, Error> {
fn index_async(req: HttpRequest) -> impl Future<Item = HttpResponse, Error = Error> {
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<String>) -> HttpResponse {
fn index_async_body(path: web::Path<String>) -> 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()
}

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

@ -2,9 +2,9 @@
name = "cookie-auth"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

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

View File

@ -2,12 +2,13 @@
name = "cookie-session"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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::<i32>("counter")? {
if let Some(count) = session.get::<i32>("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()
}

View File

@ -2,15 +2,14 @@
name = "diesel-example"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<ConnectionManager<SqliteConnection>>);
/// 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<models::User, Error>;
}
impl Actor for DbExecutor {
type Context = SyncContext<Self>;
}
impl Handler<CreateUser> for DbExecutor {
type Result = Result<models::User, Error>;
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::<models::User>(conn)
.map_err(|_| error::ErrorInternalServerError("Error loading person"))?;
Ok(items.pop().unwrap())
}
}

View File

@ -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<ConnectionManager<SqliteConnection>>;
/// State with DbExecutor address
struct AppState {
db: Addr<DbExecutor>,
/// Diesel query
fn query(
nm: String,
pool: web::Data<Pool>,
) -> Result<models::User, diesel::result::Error> {
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::<models::User>(conn)?;
Ok(items.pop().unwrap())
}
/// Async request handler
fn add(
(name, state): (Path<String>, State<AppState>),
) -> FutureResponse<HttpResponse> {
// 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<String>,
pool: web::Data<Pool>,
) -> impl Future<Item = HttpResponse, Error = Error> {
// 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<AppState>, State<AppState>)) -> impl Future<Item = HttpResponse, Error = Error> {
// HttpRequest::payload() is stream of Bytes objects
req.payload()
fn index_add(
pl: web::Payload,
pool: web::Data<Pool>,
) -> impl Future<Item = HttpResponse, Error = Error> {
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<AppState>, State<AppState>)) -> 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<Future<Item = HttpResponse, Error = Error>> {
.and_then(move |body| {
// body is loaded, now we can deserialize serde-json
let r_obj = serde_json::from_slice::<MyUser>(&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<MyUser>, State<AppState>)) -> impl Future<Item = HttpResponse, Error = Error> {
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<MyUser>,
pool: web::Data<Pool>,
) -> impl Future<Item = HttpResponse, Error = Error> {
// 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::<SqliteConnection>::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()
}

View File

@ -2,11 +2,13 @@
name = "error_handling"
version = "0.1.0"
authors = ["dowwie <dkcdkg@gmail.com>"]
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"

View File

@ -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<CustomError> for Standard {
fn sample<R: Rng + ?Sized>(&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<Item = Result<(), FailureError>,
// Error = ActixWebError> {
fn do_something_random() -> impl Future<Item = (), Error = CustomError> {
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::<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::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()
}

View File

@ -2,10 +2,11 @@
name = "form-example"
version = "0.1.0"
authors = ["Gorm Casper <gcasper@gmail.com>"]
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"

View File

@ -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<AppState>) -> Result<HttpResponse> {
Ok(HttpResponse::build(http::StatusCode::OK)
fn index() -> Result<HttpResponse> {
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<MyParams>) -> Result<HttpResponse> {
Ok(HttpResponse::build(http::StatusCode::OK)
fn handle_post_1(params: web::Form<MyParams>) -> Result<HttpResponse> {
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<AppState>, Form<MyParams>),
) -> Result<HttpResponse> {
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<AppState>,
params: web::Form<MyParams>,
) -> 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<AppState>, Form<MyParams>),
) -> Result<HttpResponse> {
fn handle_post_3(req: HttpRequest, params: web::Form<MyParams>) -> 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))
}

View File

@ -2,10 +2,9 @@
name = "hello-world"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

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

View File

@ -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"

View File

@ -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<AppState>,
) -> Box<Future<Item = HttpResponse, Error = Error>> {
let mut new_url = req.state().forward_url.clone();
req: HttpRequest,
payload: web::Payload,
url: web::Data<Url>,
client: web::Data<Client>,
) -> impl Future<Item = HttpResponse, Error = Error> {
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<dyn Future<Item = HttpResponse, Error = Error>> {
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()
}

View File

@ -2,7 +2,8 @@
name = "http-proxy"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<Future<Item = HttpResponse, Error = Error>> {
client::ClientRequest::get("http://127.0.0.1:8081/")
.finish().unwrap()
fn index(client: web::Data<Client>) -> impl Future<Item = HttpResponse, Error = Error> {
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<Future<Item = HttpResponse, Error = Error>> {
fn streaming(
client: web::Data<Client>,
) -> impl Future<Item = HttpResponse, Error = impl Into<Error>> {
// 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()
}

View File

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

View File

@ -2,9 +2,12 @@
name = "json-example"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<Future<Item = HttpResponse, Error = Error>> {
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<MyObj>) -> HttpResponse {
fn index(item: web::Json<MyObj>) -> 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<MyObj>, HttpRequest)) -> HttpResponse {
println!("model: {:?}", &item);
HttpResponse::Ok().json(item.0) // <- send response
fn extract_item(item: web::Json<MyObj>, 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<Future<Item = HttpResponse, Error = Error>> {
// HttpRequest::payload() is stream of Bytes objects
req.payload()
fn index_manual(
payload: web::Payload,
) -> impl Future<Item = HttpResponse, Error = Error> {
// 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<Future<Item = HttpResponse, Error = Er
let obj = serde_json::from_slice::<MyObj>(&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<Future<Item = HttpResponse, Error = Error>> {
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<Item = HttpResponse, Error = Error> {
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()
}

View File

@ -2,17 +2,14 @@
name = "juniper-example"
version = "0.1.0"
authors = ["pyros2097 <pyros2097@gmail.com>"]
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"

View File

@ -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<GraphQLExecutor>,
}
#[derive(Serialize, Deserialize)]
pub struct GraphQLData(GraphQLRequest);
impl Message for GraphQLData {
type Result = Result<String, Error>;
}
pub struct GraphQLExecutor {
schema: std::sync::Arc<Schema>,
}
impl GraphQLExecutor {
fn new(schema: std::sync::Arc<Schema>) -> GraphQLExecutor {
GraphQLExecutor { schema: schema }
}
}
impl Actor for GraphQLExecutor {
type Context = SyncContext<Self>;
}
impl Handler<GraphQLData> for GraphQLExecutor {
type Result = Result<String, Error>;
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<AppState>) -> Result<HttpResponse, Error> {
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<AppState>, Json<GraphQLData>),
) -> FutureResponse<HttpResponse> {
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<Arc<Schema>>,
data: web::Json<GraphQLRequest>,
) -> impl Future<Item = HttpResponse, Error = Error> {
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()
}

View File

@ -2,7 +2,11 @@
name = "middleware-example"
version = "0.1.0"
authors = ["Gorm Casper <gcasper@gmail.com>"]
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"

View File

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

View File

@ -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<S> Middleware<S> for CheckLogin {
// We only need to hook into the `start` for this middleware.
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
impl<S, P, B> Transform<S> for CheckLogin
where
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
S::Future: 'static,
{
type Request = ServiceRequest<P>;
type Response = ServiceResponse<B>;
type Error = S::Error;
type InitError = ();
type Transform = CheckLoginMiddleware<S>;
type Future = FutureResult<Self::Transform, Self::InitError>;
fn new_transform(&self, service: S) -> Self::Future {
ok(CheckLoginMiddleware { service })
}
}
pub struct CheckLoginMiddleware<S> {
service: S,
}
impl<S, P, B> Service for CheckLoginMiddleware<S>
where
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
S::Future: 'static,
{
type Request = ServiceRequest<P>;
type Response = ServiceResponse<B>;
type Error = S::Error;
type Future = Either<S::Future, FutureResult<Self::Response, Self::Error>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready()
}
fn call(&mut self, req: ServiceRequest<P>) -> 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(),
))
}
}

View File

@ -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<S> Middleware<S> for SayHi {
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
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<S, P, B> Transform<S> for SayHi
where
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
S::Future: 'static,
S::Error: 'static,
B: 'static,
{
type Request = ServiceRequest<P>;
type Response = ServiceResponse<B>;
type Error = S::Error;
type InitError = ();
type Transform = SayHiMiddleware<S>;
type Future = FutureResult<Self::Transform, Self::InitError>;
fn response(&self, _req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
println!("Hi from response");
Ok(Response::Done(resp))
}
fn finish(&self, _req: &HttpRequest<S>, _resp: &HttpResponse) -> Finished {
println!("Hi from finish");
Finished::Done
fn new_transform(&self, service: S) -> Self::Future {
ok(SayHiMiddleware { service })
}
}
pub struct SayHiMiddleware<S> {
service: S,
}
impl<S, P, B> Service for SayHiMiddleware<S>
where
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
S::Future: 'static,
S::Error: 'static,
B: 'static,
{
type Request = ServiceRequest<P>;
type Response = ServiceResponse<B>;
type Error = S::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready()
}
fn call(&mut self, req: ServiceRequest<P>) -> 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)
}))
}
}

View File

@ -2,15 +2,16 @@
name = "multipart-example"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<usize>,
}
pub fn save_file(
field: multipart::Field<dev::Payload>,
) -> Box<Future<Item = i64, Error = Error>> {
pub fn save_file(field: web::MultipartField) -> impl Future<Item = i64, Error = Error> {
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<dev::Payload>,
item: web::MultipartItem,
) -> Box<Stream<Item = i64, Error = Error>> {
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<AppState>) -> FutureResponse<HttpResponse> {
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<Cell<usize>>,
) -> impl Future<Item = HttpResponse, Error = Error> {
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<AppState>) -> Result<HttpResponse, error::Error> {
fn index() -> HttpResponse {
let html = r#"<html>
<head><title>Upload Test</title></head>
<body>
@ -90,26 +76,23 @@ fn index(_req: HttpRequest<AppState>) -> Result<HttpResponse, error::Error> {
</body>
</html>"#;
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()
}

View File

@ -28,10 +28,10 @@ pub struct MyObj {
/// This handler uses `ProtoBufMessage` for loading protobuf object.
fn index(req: &HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> {
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();

View File

@ -2,20 +2,17 @@
name = "r2d2-example"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<SqliteConnectionManager>);
/// 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<String, io::Error>;
}
impl Actor for DbExecutor {
type Context = SyncContext<Self>;
}
impl Handler<CreateUser> for DbExecutor {
type Result = Result<String, io::Error>;
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"))?)
}
}

View File

@ -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<String>,
db: web::Data<Pool<SqliteConnectionManager>>,
) -> impl Future<Item = HttpResponse, Error = Error> {
// execute sync code in threadpool
web::block(move || {
let conn = db.get().unwrap();
/// State with DbExecutor address
struct State {
db: Addr<DbExecutor>,
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<State>) -> Box<Future<Item = HttpResponse, Error = Error>> {
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()
}

View File

@ -2,10 +2,12 @@
name = "redis-session"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<HttpResponse> {
fn index(req: HttpRequest, session: Session) -> Result<HttpResponse> {
println!("{:?}", req);
// session
if let Some(count) = req.session().get::<i32>("counter")? {
if let Some(count) = session.get::<i32>("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()
}

View File

@ -1,5 +1,2 @@
max_width = 89
reorder_imports = true
#wrap_comments = true
fn_args_density = "Compressed"
#use_small_heuristics = false

View File

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

View File

@ -2,18 +2,22 @@
name = "simple-auth-server"
version = "0.1.0"
authors = ["mygnu <tech@hgill.io>"]
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"

View File

@ -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<DbExecutor>,
}
/// creates and returns the app after mounting all routes/resources
pub fn create_app(db: Addr<DbExecutor>) -> App<AppState> {
// 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"),
)
}

View File

@ -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<AuthData> for DbExecutor {
type Result = Result<SlimUser, ServiceError>;
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::<User>(conn)?;
let mut items = users.filter(email.eq(&msg.email)).load::<User>(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<AuthData> for DbExecutor {
// simple aliasing makes the intentions clear and its more readable
pub type LoggedUser = SlimUser;
impl<S> FromRequest<S> for LoggedUser {
type Config = ();
type Result = Result<LoggedUser, ServiceError>;
fn from_request(req: &HttpRequest<S>, _: &Self::Config) -> Self::Result {
if let Some(identity) = req.identity() {
impl<P> FromRequest<P> for LoggedUser {
type Error = Error;
type Future = Result<LoggedUser, Error>;
fn from_request(req: &mut ServiceFromRequest<P>) -> 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())
}
}

View File

@ -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<AuthData>, HttpRequest<AppState>))
-> FutureResponse<HttpResponse> {
req.state()
.db
.send(auth_data.into_inner())
pub fn login(
auth_data: web::Json<AuthData>,
id: Identity,
db: web::Data<Addr<DbExecutor>>,
) -> impl Future<Item = HttpResponse, Error = Error> {
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<AppState>) -> 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 {

View File

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

View File

@ -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<Error> 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,
}
}
}

View File

@ -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<CreateInvitation> for DbExecutor {
type Result = Result<Invitation, ServiceError>;
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<CreateInvitation> for DbExecutor {
Ok(inserted_invitation)
}
}

View File

@ -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<CreateInvitation>, State<AppState>),
) -> FutureResponse<HttpResponse> {
state
.db
.send(signup_invitation.into_inner())
signup_invitation: web::Json<CreateInvitation>,
db: web::Data<Addr<DbExecutor>>,
) -> impl Future<Item = HttpResponse, Error = Error> {
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()
})
}

View File

@ -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::<PgConnection>::new(database_url);
@ -55,12 +46,52 @@ fn main() {
.build(manager)
.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()))
.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()
}

View File

@ -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<ConnectionManager<PgConnection>>);
@ -51,8 +51,6 @@ pub struct SlimUser {
impl From<User> for SlimUser {
fn from(user: User) -> Self {
SlimUser {
email: user.email
}
SlimUser { email: user.email }
}
}

View File

@ -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<SlimUser, ServiceError>;
}
impl Handler<RegisterUser> for DbExecutor {
type Result = Result<SlimUser, ServiceError>;
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::<Invitation>(conn)
.map_err(|_db_error| ServiceError::BadRequest("Invalid Invitation".into()))
.and_then(|mut result| {
@ -45,9 +46,8 @@ impl Handler<RegisterUser> 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<RegisterUser> for DbExecutor {
})
}
}

View File

@ -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<String>, Json<UserData>, State<AppState>))
-> FutureResponse<HttpResponse> {
pub fn register_user(
invitation_id: web::Path<String>,
user_data: web::Json<UserData>,
db: web::Data<Addr<DbExecutor>>,
) -> impl Future<Item = HttpResponse, Error = Error> {
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()
})
}

View File

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

View File

@ -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<String, ServiceError> {
// 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<SlimUser, ServiceError> {
}
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())
}

View File

@ -2,11 +2,10 @@
name = "state"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<Mutex<usize>>,
}
use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer};
/// simple handle
fn index(req: &HttpRequest<AppState>) -> HttpResponse {
fn index(state: web::Data<Arc<Mutex<usize>>>, 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()
}

View File

@ -2,11 +2,12 @@
name = "static_index"
version = "0.1.0"
authors = ["Jose Marinez <digeratus@gmail.com>"]
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"

View File

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

View File

@ -2,14 +2,14 @@
name = "template-askama"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<HashMap<String, String>>) -> Result<HttpResponse> {
if let Some(name) = query.get("name") {
fn index(query: web::Query<HashMap<String, String>>) -> Result<HttpResponse> {
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()
}

View File

@ -2,10 +2,10 @@
name = "template-tera"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<AppState>, Query<HashMap<String, String>>),
tmpl: web::Data<tera::Tera>,
query: web::Query<HashMap<String, String>>,
) -> Result<HttpResponse, Error> {
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()
}

15
template_yarte/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "example"
version = "0.0.1"
authors = ["Rust-iendo Barcelona <riendocontributions@gmail.com>"]
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"

11
template_yarte/README.md Normal file
View File

@ -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)

5
template_yarte/build.rs Normal file
View File

@ -0,0 +1,5 @@
use yarte::recompile;
fn main() {
recompile::when_changed();
}

View File

@ -0,0 +1 @@

View File

@ -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<HashMap<String, String>>,
}
#[get("/")]
pub fn index(query: Query<HashMap<String, String>>) -> Result<HttpResponse> {
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()
}

View File

@ -0,0 +1 @@
<{{ tag }}>Welcome!</{{ tag }}>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Actix web</title>
</head>
<body>
{{~#if let Some(name) = query.get("name") ~}}
<h1>Hi, {{ name }}!</h1>
{{~> alias/welcome tag='p' ~}}
{{~ else ~}}
{{~> alias/welcome tag="h1" ~}}
<p>
<h3>What is your name?</h3>
<form>
<input type="text" name="name"/><br/>
<p><input type="submit"></p>
</form>
</p>
{{~/if~}}
</body>
</html>

View File

@ -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"

View File

@ -1,15 +1,16 @@
[package]
name = "tls-example"
version = "0.1.0"
version = "0.2.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"] }

View File

@ -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-----

View File

@ -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-----

View File

@ -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<HttpResponse, Error> {
fn index(req: HttpRequest) -> Result<HttpResponse, Error> {
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()
}

View File

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

View File

@ -3,16 +3,13 @@ name = "actix-web-cors"
version = "0.1.0"
authors = ["krircc <krircc@aliyun.com>"]
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"

View File

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

View File

@ -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<Info>) -> Result<Json<Info>> {
pub fn info(info: web::Json<Info>) -> web::Json<Info> {
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(),
}))
})
}

View File

@ -52,7 +52,8 @@ impl WsChatSession {
}
fut::ok(())
}).spawn(ctx);
})
.spawn(ctx);
}
fn list_rooms(&mut self, ctx: &mut ws::WebsocketContext<Self>) {
@ -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();

View File

@ -2,23 +2,24 @@
name = "websocket-example"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

@ -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<server::ChatServer>,
}
/// Entry point for our route
fn chat_route(req: &HttpRequest<WsChatSessionState>) -> Result<HttpResponse, Error> {
fn chat_route(
req: HttpRequest,
stream: web::Payload,
srv: web::Data<Addr<server::ChatServer>>,
) -> Result<HttpResponse, Error> {
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<String>,
/// Chat server
addr: Addr<server::ChatServer>,
}
impl Actor for WsChatSession {
type Context = ws::WebsocketContext<Self, WsChatSessionState>;
type Context = ws::WebsocketContext<Self>;
/// 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<ws::Message, ws::ProtocolError> 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<ws::Message, ws::ProtocolError> 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<ws::Message, ws::ProtocolError> 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<Self, WsChatSessionState>) {
fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) {
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()
}

View File

@ -2,7 +2,8 @@
name = "websocket-tcp-example"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
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"

View File

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

View File

@ -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<server::ChatServer>,
}
/// Entry point for our route
fn chat_route(req: &HttpRequest<WsChatSessionState>) -> Result<HttpResponse, Error> {
fn chat_route(
req: HttpRequest,
stream: web::Payload,
srv: web::Data<Addr<server::ChatServer>>,
) -> Result<HttpResponse, Error> {
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<String>,
/// Chat server
addr: Addr<server::ChatServer>,
}
impl Actor for WsChatSession {
type Context = ws::WebsocketContext<Self, WsChatSessionState>;
type Context = ws::WebsocketContext<Self>;
/// 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<ws::Message, ws::ProtocolError> 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<ws::Message, ws::ProtocolError> 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<ws::Message, ws::ProtocolError> 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<Self, WsChatSessionState>) {
fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) {
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>(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()
}

Some files were not shown because too many files have changed in this diff Show More