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:
commit
9c8389e06e
@ -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 ..
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()),
|
||||
))
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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"
|
||||
|
@ -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),
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
@ -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),
|
||||
})
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
127
json/src/main.rs
127
json/src/main.rs
@ -1,22 +1,13 @@
|
||||
extern crate actix;
|
||||
extern crate actix_web;
|
||||
extern crate bytes;
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate serde_json;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate json;
|
||||
|
||||
use actix_web::{
|
||||
error, http, middleware, server, App, AsyncResponder, Error, HttpMessage,
|
||||
HttpRequest, HttpResponse, Json,
|
||||
error, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer,
|
||||
};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use futures::{Future, Stream};
|
||||
use json::JsonValue;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct MyObj {
|
||||
@ -24,39 +15,31 @@ struct MyObj {
|
||||
number: i32,
|
||||
}
|
||||
|
||||
/// This handler uses `HttpRequest::json()` for loading json object.
|
||||
fn index(req: &HttpRequest) -> Box<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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
@ -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()
|
||||
}
|
||||
|
@ -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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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"))?)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -1,5 +1,2 @@
|
||||
max_width = 89
|
||||
reorder_imports = true
|
||||
#wrap_comments = true
|
||||
fn_args_density = "Compressed"
|
||||
#use_small_heuristics = false
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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"),
|
||||
)
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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()
|
||||
})
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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()
|
||||
})
|
||||
}
|
||||
|
@ -14,7 +14,4 @@ table! {
|
||||
}
|
||||
}
|
||||
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
invitations,
|
||||
users,
|
||||
);
|
||||
allow_tables_to_appear_in_same_query!(invitations, users,);
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
15
template_yarte/Cargo.toml
Normal 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
11
template_yarte/README.md
Normal 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
5
template_yarte/build.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use yarte::recompile;
|
||||
|
||||
fn main() {
|
||||
recompile::when_changed();
|
||||
}
|
1
template_yarte/src/lib.rs
Normal file
1
template_yarte/src/lib.rs
Normal file
@ -0,0 +1 @@
|
||||
|
43
template_yarte/src/main.rs
Normal file
43
template_yarte/src/main.rs
Normal 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()
|
||||
}
|
1
template_yarte/templates/deep/more/deep/welcome.hbs
Normal file
1
template_yarte/templates/deep/more/deep/welcome.hbs
Normal file
@ -0,0 +1 @@
|
||||
<{{ tag }}>Welcome!</{{ tag }}>
|
22
template_yarte/templates/index.hbs
Normal file
22
template_yarte/templates/index.hbs
Normal 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>
|
7
template_yarte/yarte.toml
Normal file
7
template_yarte/yarte.toml
Normal 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"
|
@ -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"] }
|
||||
|
43
tls/cert.pem
43
tls/cert.pem
@ -1,31 +1,16 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFPjCCAyYCCQDvLYiYD+jqeTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV
|
||||
UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMRAwDgYDVQQKDAdDb21wYW55MQww
|
||||
CgYDVQQLDANPcmcxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xODAxMjUx
|
||||
NzQ2MDFaFw0xOTAxMjUxNzQ2MDFaMGExCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJD
|
||||
QTELMAkGA1UEBwwCU0YxEDAOBgNVBAoMB0NvbXBhbnkxDDAKBgNVBAsMA09yZzEY
|
||||
MBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
|
||||
MIICCgKCAgEA2WzIA2IpVR9Tb9EFhITlxuhE5rY2a3S6qzYNzQVgSFggxXEPn8k1
|
||||
sQEcer5BfAP986Sck3H0FvB4Bt/I8PwOtUCmhwcc8KtB5TcGPR4fjXnrpC+MIK5U
|
||||
NLkwuyBDKziYzTdBj8kUFX1WxmvEHEgqToPOZfBgsS71cJAR/zOWraDLSRM54jXy
|
||||
voLZN4Ti9rQagQrvTQ44Vz5ycDQy7UxtbUGh1CVv69vNVr7/SOOh/Nw5FNOZWLWr
|
||||
odGyoec5wh9iqRZgRqiTUc6Lt7V2RWc2X2gjwST2UfI+U46Ip3oaQ7ZD4eAkoqND
|
||||
xdniBZAykVG3c/99ux4BAESTF8fsNch6UticBxYMuTu+ouvP0psfI9wwwNliJDmA
|
||||
CRUTB9AgRynbL1AzhqQoDfsb98IZfjfNOpwnwuLwpMAPhbgd5KNdZaIJ4Hb6/stI
|
||||
yFElOExxd3TAxF2Gshd/lq1JcNHAZ1DSXV5MvOWT/NWgXwbIzUgQ8eIi+HuDYX2U
|
||||
UuaB6R8tbd52H7rbUv6HrfinuSlKWqjSYLkiKHkwUpoMw8y9UycRSzs1E9nPwPTO
|
||||
vRXb0mNCQeBCV9FvStNVXdCUTT8LGPv87xSD2pmt7LijlE6mHLG8McfcWkzA69un
|
||||
CEHIFAFDimTuN7EBljc119xWFTcHMyoZAfFF+oTqwSbBGImruCxnaJECAwEAATAN
|
||||
BgkqhkiG9w0BAQsFAAOCAgEApavsgsn7SpPHfhDSN5iZs1ILZQRewJg0Bty0xPfk
|
||||
3tynSW6bNH3nSaKbpsdmxxomthNSQgD2heOq1By9YzeOoNR+7Pk3s4FkASnf3ToI
|
||||
JNTUasBFFfaCG96s4Yvs8KiWS/k84yaWuU8c3Wb1jXs5Rv1qE1Uvuwat1DSGXSoD
|
||||
JNluuIkCsC4kWkyq5pWCGQrabWPRTWsHwC3PTcwSRBaFgYLJaR72SloHB1ot02zL
|
||||
d2age9dmFRFLLCBzP+D7RojBvL37qS/HR+rQ4SoQwiVc/JzaeqSe7ZbvEH9sZYEu
|
||||
ALowJzgbwro7oZflwTWunSeSGDSltkqKjvWvZI61pwfHKDahUTmZ5h2y67FuGEaC
|
||||
CIOUI8dSVSPKITxaq3JL4ze2e9/0Lt7hj19YK2uUmtMAW5Tirz4Yx5lyGH9U8Wur
|
||||
y/X8VPxTc4A9TMlJgkyz0hqvhbPOT/zSWB10zXh0glKAsSBryAOEDxV1UygmSir7
|
||||
YV8Qaq+oyKUTMc1MFq5vZ07M51EPaietn85t8V2Y+k/8XYltRp32NxsypxAJuyxh
|
||||
g/ko6RVTrWa1sMvz/F9LFqAdKiK5eM96lh9IU4xiLg4ob8aS/GRAA8oIFkZFhLrt
|
||||
tOwjIUPmEPyHWFi8dLpNuQKYalLYhuwZftG/9xV+wqhKGZO9iPrpHSYBRTap8w2y
|
||||
1QU=
|
||||
MIICljCCAX4CCQDztMNlxk6oeTANBgkqhkiG9w0BAQsFADANMQswCQYDVQQIDAJj
|
||||
YTAeFw0xOTAzMDcwNzEyNThaFw0yMDAzMDYwNzEyNThaMA0xCzAJBgNVBAgMAmNh
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0GMP3YzDVFWgNhRiHnfe
|
||||
d192131Zi23p8WiutneD9I5WO42c79fOXsxLWn+2HSqPvCPHIBLoMX8o9lgCxt2P
|
||||
/JUCAWbrE2EuvhkMrWk6/q7xB211XZYfnkqdt7mA0jMUC5o32AX3ew456TAq5P8Y
|
||||
dq9H/qXdRtAvKD0QdkFfq8ePCiqOhcqacZ/NWva7R4HdgTnbL1DRQjGBXszI07P9
|
||||
1yw8GOym46uxNHRujQp3lYEhc1V3JTF9kETpSBHyEAkQ8WHxGf8UBHDhh7hcc+KI
|
||||
JHMlVYy5wDv4ZJeYsY1rD6/n4tyd3r0yzBM57UGf6qrVZEYmLB7Jad+8Df5vIoGh
|
||||
WwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB1DEu9NiShCfQuA17MG5O0Jr2/PS1z
|
||||
/+HW7oW15WXpqDKOEJalid31/Bzwvwq0bE12xKE4ZLdbqJHmJTdSUoGfOfBZKka6
|
||||
R2thOjqH7hFvxjfgS7kBy5BrRZewM9xKIJ6zU6+6mxR64x9vmkOmppV0fx5clZjH
|
||||
c7qn5kSNWTMsFbjPnb5BeJJwZdqpMLs99jgoMvGtCUmkyVYODGhh65g6tR9kIPvM
|
||||
zu/Cw122/y7tFfkuknMSYwGEYF3XcZpXt54a6Lu5hk6PuOTsK+7lC+HX7CSF1dpv
|
||||
u1szL5fDgiCBFCnyKeOqF61mxTCUht3U++37VDFvhzN1t6HIVTYm2JJ7
|
||||
-----END CERTIFICATE-----
|
||||
|
79
tls/key.pem
79
tls/key.pem
@ -1,51 +1,28 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJKAIBAAKCAgEA2WzIA2IpVR9Tb9EFhITlxuhE5rY2a3S6qzYNzQVgSFggxXEP
|
||||
n8k1sQEcer5BfAP986Sck3H0FvB4Bt/I8PwOtUCmhwcc8KtB5TcGPR4fjXnrpC+M
|
||||
IK5UNLkwuyBDKziYzTdBj8kUFX1WxmvEHEgqToPOZfBgsS71cJAR/zOWraDLSRM5
|
||||
4jXyvoLZN4Ti9rQagQrvTQ44Vz5ycDQy7UxtbUGh1CVv69vNVr7/SOOh/Nw5FNOZ
|
||||
WLWrodGyoec5wh9iqRZgRqiTUc6Lt7V2RWc2X2gjwST2UfI+U46Ip3oaQ7ZD4eAk
|
||||
oqNDxdniBZAykVG3c/99ux4BAESTF8fsNch6UticBxYMuTu+ouvP0psfI9wwwNli
|
||||
JDmACRUTB9AgRynbL1AzhqQoDfsb98IZfjfNOpwnwuLwpMAPhbgd5KNdZaIJ4Hb6
|
||||
/stIyFElOExxd3TAxF2Gshd/lq1JcNHAZ1DSXV5MvOWT/NWgXwbIzUgQ8eIi+HuD
|
||||
YX2UUuaB6R8tbd52H7rbUv6HrfinuSlKWqjSYLkiKHkwUpoMw8y9UycRSzs1E9nP
|
||||
wPTOvRXb0mNCQeBCV9FvStNVXdCUTT8LGPv87xSD2pmt7LijlE6mHLG8McfcWkzA
|
||||
69unCEHIFAFDimTuN7EBljc119xWFTcHMyoZAfFF+oTqwSbBGImruCxnaJECAwEA
|
||||
AQKCAgAME3aoeXNCPxMrSri7u4Xnnk71YXl0Tm9vwvjRQlMusXZggP8VKN/KjP0/
|
||||
9AE/GhmoxqPLrLCZ9ZE1EIjgmZ9Xgde9+C8rTtfCG2RFUL7/5J2p6NonlocmxoJm
|
||||
YkxYwjP6ce86RTjQWL3RF3s09u0inz9/efJk5O7M6bOWMQ9VZXDlBiRY5BYvbqUR
|
||||
6FeSzD4MnMbdyMRoVBeXE88gTvZk8xhB6DJnLzYgc0tKiRoeKT0iYv5JZw25VyRM
|
||||
ycLzfTrFmXCPfB1ylb483d9Ly4fBlM8nkx37PzEnAuukIawDxsPOb9yZC+hfvNJI
|
||||
7NFiMN+3maEqG2iC00w4Lep4skHY7eHUEUMl+Wjr+koAy2YGLWAwHZQTm7iXn9Ab
|
||||
L6adL53zyCKelRuEQOzbeosJAqS+5fpMK0ekXyoFIuskj7bWuIoCX7K/kg6q5IW+
|
||||
vC2FrlsrbQ79GztWLVmHFO1I4J9M5r666YS0qdh8c+2yyRl4FmSiHfGxb3eOKpxQ
|
||||
b6uI97iZlkxPF9LYUCSc7wq0V2gGz+6LnGvTHlHrOfVXqw/5pLAKhXqxvnroDTwz
|
||||
0Ay/xFF6ei/NSxBY5t8ztGCBm45wCU3l8pW0X6dXqwUipw5b4MRy1VFRu6rqlmbL
|
||||
OPSCuLxqyqsigiEYsBgS/icvXz9DWmCQMPd2XM9YhsHvUq+R4QKCAQEA98EuMMXI
|
||||
6UKIt1kK2t/3OeJRyDd4iv/fCMUAnuPjLBvFE4cXD/SbqCxcQYqb+pue3PYkiTIC
|
||||
71rN8OQAc5yKhzmmnCE5N26br/0pG4pwEjIr6mt8kZHmemOCNEzvhhT83nfKmV0g
|
||||
9lNtuGEQMiwmZrpUOF51JOMC39bzcVjYX2Cmvb7cFbIq3lR0zwM+aZpQ4P8LHCIu
|
||||
bgHmwbdlkLyIULJcQmHIbo6nPFB3ZZE4mqmjwY+rA6Fh9rgBa8OFCfTtrgeYXrNb
|
||||
IgZQ5U8GoYRPNC2ot0vpTinraboa/cgm6oG4M7FW1POCJTl+/ktHEnKuO5oroSga
|
||||
/BSg7hCNFVaOhwKCAQEA4Kkys0HtwEbV5mY/NnvUD5KwfXX7BxoXc9lZ6seVoLEc
|
||||
KjgPYxqYRVrC7dB2YDwwp3qcRTi/uBAgFNm3iYlDzI4xS5SeaudUWjglj7BSgXE2
|
||||
iOEa7EwcvVPluLaTgiWjlzUKeUCNNHWSeQOt+paBOT+IgwRVemGVpAgkqQzNh/nP
|
||||
tl3p9aNtgzEm1qVlPclY/XUCtf3bcOR+z1f1b4jBdn0leu5OhnxkC+Htik+2fTXD
|
||||
jt6JGrMkanN25YzsjnD3Sn+v6SO26H99wnYx5oMSdmb8SlWRrKtfJHnihphjG/YY
|
||||
l1cyorV6M/asSgXNQfGJm4OuJi0I4/FL2wLUHnU+JwKCAQEAzh4WipcRthYXXcoj
|
||||
gMKRkMOb3GFh1OpYqJgVExtudNTJmZxq8GhFU51MR27Eo7LycMwKy2UjEfTOnplh
|
||||
Us2qZiPtW7k8O8S2m6yXlYUQBeNdq9IuuYDTaYD94vsazscJNSAeGodjE+uGvb1q
|
||||
1wLqE87yoE7dUInYa1cOA3+xy2/CaNuviBFJHtzOrSb6tqqenQEyQf6h9/12+DTW
|
||||
t5pSIiixHrzxHiFqOoCLRKGToQB+71rSINwTf0nITNpGBWmSj5VcC3VV3TG5/XxI
|
||||
fPlxV2yhD5WFDPVNGBGvwPDSh4jSMZdZMSNBZCy4XWFNSKjGEWoK4DFYed3DoSt9
|
||||
5IG1YwKCAQA63ntHl64KJUWlkwNbboU583FF3uWBjee5VqoGKHhf3CkKMxhtGqnt
|
||||
+oN7t5VdUEhbinhqdx1dyPPvIsHCS3K1pkjqii4cyzNCVNYa2dQ00Qq+QWZBpwwc
|
||||
3GAkz8rFXsGIPMDa1vxpU6mnBjzPniKMcsZ9tmQDppCEpBGfLpio2eAA5IkK8eEf
|
||||
cIDB3CM0Vo94EvI76CJZabaE9IJ+0HIJb2+jz9BJ00yQBIqvJIYoNy9gP5Xjpi+T
|
||||
qV/tdMkD5jwWjHD3AYHLWKUGkNwwkAYFeqT/gX6jpWBP+ZRPOp011X3KInJFSpKU
|
||||
DT5GQ1Dux7EMTCwVGtXqjO8Ym5wjwwsfAoIBAEcxlhIW1G6BiNfnWbNPWBdh3v/K
|
||||
5Ln98Rcrz8UIbWyl7qNPjYb13C1KmifVG1Rym9vWMO3KuG5atK3Mz2yLVRtmWAVc
|
||||
fxzR57zz9MZFDun66xo+Z1wN3fVxQB4CYpOEI4Lb9ioX4v85hm3D6RpFukNtRQEc
|
||||
Gfr4scTjJX4jFWDp0h6ffMb8mY+quvZoJ0TJqV9L9Yj6Ksdvqez/bdSraev97bHQ
|
||||
4gbQxaTZ6WjaD4HjpPQefMdWp97Metg0ZQSS8b8EzmNFgyJ3XcjirzwliKTAQtn6
|
||||
I2sd0NCIooelrKRD8EJoDUwxoOctY7R97wpZ7/wEHU45cBCbRV3H4JILS5c=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDQYw/djMNUVaA2
|
||||
FGIed953X3bXfVmLbenxaK62d4P0jlY7jZzv185ezEtaf7YdKo+8I8cgEugxfyj2
|
||||
WALG3Y/8lQIBZusTYS6+GQytaTr+rvEHbXVdlh+eSp23uYDSMxQLmjfYBfd7Djnp
|
||||
MCrk/xh2r0f+pd1G0C8oPRB2QV+rx48KKo6Fyppxn81a9rtHgd2BOdsvUNFCMYFe
|
||||
zMjTs/3XLDwY7Kbjq7E0dG6NCneVgSFzVXclMX2QROlIEfIQCRDxYfEZ/xQEcOGH
|
||||
uFxz4ogkcyVVjLnAO/hkl5ixjWsPr+fi3J3evTLMEzntQZ/qqtVkRiYsHslp37wN
|
||||
/m8igaFbAgMBAAECggEAJI278rkGany6pcHdlEqik34DcrliQ7r8FoSuYQOF+hgd
|
||||
uESXCttoL+jWLwHICEW3AOGlxFKMuGH95Xh6xDeJUl0xBN3wzm11rZLnTmPvHU3C
|
||||
qfLha5Ex6qpcECZSGo0rLv3WXeZuCv/r2KPCYnj86ZTFpD2kGw/Ztc1AXf4Jsi/1
|
||||
478Mf23QmAvCAPimGCyjLQx2c9/vg/6K7WnDevY4tDuDKLeSJxKZBSHUn3cM1Bwj
|
||||
2QzaHfSFA5XljOF5PLeR3cY5ncrrVLWChT9XuGt9YMdLAcSQxgE6kWV1RSCq+lbj
|
||||
e6OOe879IrrqwBvMQfKQqnm1kl8OrfPMT5CNWKvEgQKBgQD8q5E4x9taDS9RmhRO
|
||||
07ptsr/I795tX8CaJd/jc4xGuCGBqpNw/hVebyNNYQvpiYzDNBSEhtd59957VyET
|
||||
hcrGyxD0ByKm8F/lPgFw5y6wi3RUnucCV/jxkMHmxVzYMbFUEGCQ0pIU9/GFS7RZ
|
||||
9VjqRDeE86U3yHO+WCFoHtd8aQKBgQDTIhi0uq0oY87bUGnWbrrkR0UVRNPDG1BT
|
||||
cuXACYlv/DV/XpxPC8iPK1UwG4XaOVxodtIRjdBqvb8fUM6HSY6qll64N/4/1jre
|
||||
Ho+d4clE4tK6a9WU96CKxwHn2BrWUZJPtoldaCZJFJ7SfiHuLlqW7TtYFrOfPIjN
|
||||
ADiqK+bHIwKBgQCpfIiAVwebo0Z/bWR77+iZFxMwvT4tjdJLVGaXUvXgpjjLmtkm
|
||||
LTm2S8SZbiSodfz3H+M3dp/pj8wsXiiwyMlZifOITZT/+DPLOUmMK3cVM6ZH8QMy
|
||||
fkJd/+UhYHhECSlTI10zKByXdi4LZNnIkhwfoLzBMRI9lfeV0dYu2qlfKQKBgEVI
|
||||
kRbtk1kHt5/ceX62g3nZsV/TYDJMSkW4FJC6EHHBL8UGRQDjewMQUzogLgJ4hEx7
|
||||
gV/lS5lbftZF7CAVEU4FXjvRlAtav6KYIMTMjQGf9UrbjBEAWZxwxb1Q+y2NQxgJ
|
||||
bHZMcRPWQnAMmBHTAEM6whicCoGcmb+77Nxa37ZFAoGBALBuUNeD3fKvQR8v6GoA
|
||||
spv+RYL9TB4wz2Oe9EYSp9z5EiWlTmuvFz3zk8pHDSpntxYH5O5HJ/3OzwhHz9ym
|
||||
+DNE9AP9LW9hAzMuu7Gob1h8ShGwJVYwrQN3q/83ooUL7WSAuVOLpzJ7BFFlcCjp
|
||||
MhFvd9iOt/R0N30/3AbQXkOp
|
||||
-----END PRIVATE KEY-----
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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(),
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user