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