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