Make most requests use thread pool

Fixes #4.
This commit is contained in:
Konrad Borowski 2019-09-18 13:00:43 +02:00
parent 98b64ea4a0
commit 86693018c8
13 changed files with 310 additions and 345 deletions

13
Cargo.lock generated
View File

@ -1194,7 +1194,6 @@ dependencies = [
"scraper 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-diesel 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-executor 0.2.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
"warp 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1966,17 +1965,6 @@ dependencies = [
"tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-diesel"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
"r2d2 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-executor 0.2.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-executor"
version = "0.1.8"
@ -2571,7 +2559,6 @@ dependencies = [
"checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46"
"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f"
"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443"
"checksum tokio-diesel 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb0d9ea6827f7bf84d88c6d19fd53524585663f51d8cde4508e9cdb62fcb1f15"
"checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac"
"checksum tokio-executor 0.2.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "345c88d08e60054624ac16be7feef0596a9151b63e20de75ee771effe67c3639"
"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af"

View File

@ -21,7 +21,6 @@ pulldown-cmark = "0.5.0"
rand = "0.7.0"
reqwest = "0.9.19"
serde = { version = "1.0.88", features = ["derive"] }
tokio-diesel = "0.2.0"
tokio-executor = { version = "0.2.0-alpha.4", features = ["blocking"] }
warp = "0.1.15"

View File

@ -6,18 +6,17 @@ mod routes;
mod schema;
use diesel::prelude::*;
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
use std::env;
use std::error::Error;
type PgPool = Pool<ConnectionManager<PgConnection>>;
type Connection = PooledConnection<ConnectionManager<PgConnection>>;
fn main() -> Result<(), Box<dyn Error>> {
env_logger::init();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL required");
let pool = &*Box::leak(Box::new(
Pool::new(ConnectionManager::new(database_url)).expect("Couldn't create a connection pool"),
));
let pool = Pool::new(ConnectionManager::new(database_url))
.expect("Couldn't create a connection connection");
diesel_migrations::run_pending_migrations(&pool.get()?)?;
warp::serve(routes::routes(pool)).run(([127, 0, 0, 1], 8080));
Ok(())

View File

@ -1,9 +1,7 @@
use crate::schema::languages::dsl::*;
use crate::PgPool;
use crate::Connection;
use diesel::prelude::*;
use futures::Future;
use futures03::TryFutureExt;
use tokio_diesel::{AsyncError, AsyncRunQueryDsl};
use warp::Rejection;
#[derive(Queryable)]
pub struct Language {
@ -13,12 +11,12 @@ pub struct Language {
}
impl Language {
pub fn fetch(pool: &'static PgPool) -> impl Future<Item = Vec<Language>, Error = AsyncError> {
pub fn fetch(connection: &Connection) -> Result<Vec<Language>, Rejection> {
languages
.select((language_id, identifier, name))
.order((priority.asc(), name.asc()))
.load_async(pool)
.compat()
.load(connection)
.map_err(warp::reject::custom)
}
}

View File

@ -1,14 +1,12 @@
use crate::schema::pastes;
use crate::PgPool;
use crate::Connection;
use ammonia::Builder;
use chrono::{DateTime, Utc};
use diesel::prelude::*;
use futures::Future;
use futures03::TryFutureExt;
use lazy_static::lazy_static;
use log::info;
use pulldown_cmark::{Options, Parser};
use tokio_diesel::{AsyncError, AsyncRunQueryDsl};
use warp::Rejection;
#[derive(Queryable)]
pub struct Paste {
@ -19,16 +17,15 @@ pub struct Paste {
}
impl Paste {
pub fn delete_old(pool: &'static PgPool) -> impl Future<Item = (), Error = AsyncError> {
diesel::delete(pastes::table)
pub fn delete_old(connection: &Connection) -> Result<(), Rejection> {
let pastes = diesel::delete(pastes::table)
.filter(pastes::delete_at.lt(Utc::now()))
.execute_async(pool)
.compat()
.map(|pastes| {
if pastes > 0 {
info!("Deleted {} paste(s)", pastes);
}
})
.execute(connection)
.map_err(warp::reject::custom)?;
if pastes > 0 {
info!("Deleted {} paste(s)", pastes);
}
Ok(())
}
}

View File

@ -1,9 +1,9 @@
use crate::schema::{implementation_wrappers, implementations, languages, shared_wrappers};
use crate::PgPool;
use crate::Connection;
use diesel::prelude::*;
use futures::Future;
use futures03::prelude::*;
use serde::Serialize;
use tokio_diesel::{AsyncError, AsyncRunQueryDsl, OptionalExtension};
use tokio_executor::blocking;
use warp::http::header::CACHE_CONTROL;
use warp::{Rejection, Reply};
@ -58,24 +58,23 @@ struct JsonImplementation {
wrappers: Vec<Wrapper>,
}
pub async fn api_language(
pub fn api_language(
identifier: String,
pool: &'static PgPool,
) -> Result<impl Reply, Rejection> {
let Language { id, mode, mime } = languages::table
.filter(languages::identifier.eq(identifier))
.select((
languages::language_id,
languages::highlighter_mode,
languages::mime,
))
.get_result_async(pool)
.await
.optional()
.map_err(warp::reject::custom)?
.ok_or_else(warp::reject::not_found)?;
let (shared_wrappers, implementations) = future::try_join(
shared_wrappers::table
connection: Connection,
) -> impl Future<Item = impl Reply, Error = Rejection> {
blocking::run(move || {
let Language { id, mode, mime } = languages::table
.filter(languages::identifier.eq(identifier))
.select((
languages::language_id,
languages::highlighter_mode,
languages::mime,
))
.get_result(&connection)
.optional()
.map_err(warp::reject::custom)?
.ok_or_else(warp::reject::not_found)?;
let shared_wrappers = shared_wrappers::table
.filter(shared_wrappers::language_id.eq(id))
.select((
shared_wrappers::identifier,
@ -84,73 +83,67 @@ pub async fn api_language(
shared_wrappers::is_formatter,
))
.order(shared_wrappers::ordering)
.load_async(pool),
async {
let implementations = implementations::table
.select((
implementations::implementation_id,
implementations::identifier,
implementations::label,
))
.filter(implementations::language_id.eq(id))
.load_async(pool)
.await?;
let (implementations, implementation_wrappers) = blocking::run(move || {
let implementation_wrappers = ImplementationWrapper::belonging_to(&implementations)
.select((
implementation_wrappers::implementation_wrapper_id,
implementation_wrappers::implementation_id,
implementation_wrappers::identifier,
implementation_wrappers::label,
implementation_wrappers::is_asm,
implementation_wrappers::is_formatter,
))
.order(implementation_wrappers::ordering)
.load(&pool.get().map_err(AsyncError::Checkout)?)
.map_err(AsyncError::Error)?;
Ok((implementations, implementation_wrappers))
.load(&connection)
.map_err(warp::reject::custom)?;
let implementations = implementations::table
.select((
implementations::implementation_id,
implementations::identifier,
implementations::label,
))
.filter(implementations::language_id.eq(id))
.load(&connection)
.map_err(warp::reject::custom)?;
let implementation_wrappers = ImplementationWrapper::belonging_to(&implementations)
.select((
implementation_wrappers::implementation_wrapper_id,
implementation_wrappers::implementation_id,
implementation_wrappers::identifier,
implementation_wrappers::label,
implementation_wrappers::is_asm,
implementation_wrappers::is_formatter,
))
.order(implementation_wrappers::ordering)
.load(&connection)
.map_err(warp::reject::custom)?;
let implementations = implementation_wrappers
.grouped_by(&implementations)
.into_iter()
.zip(implementations)
.map(|(wrappers, implementation)| JsonImplementation {
identifier: implementation.identifier,
label: implementation.label,
wrappers: wrappers
.into_iter()
.map(
|ImplementationWrapper {
identifier,
label,
is_asm,
is_formatter,
..
}| {
Wrapper {
identifier,
label,
is_asm,
is_formatter,
}
},
)
.collect(),
})
.await?;
Ok(implementation_wrappers
.grouped_by(&implementations)
.into_iter()
.zip(implementations)
.map(|(wrappers, implementation)| JsonImplementation {
identifier: implementation.identifier,
label: implementation.label,
wrappers: wrappers
.into_iter()
.map(
|ImplementationWrapper {
identifier,
label,
is_asm,
is_formatter,
..
}| {
Wrapper {
identifier,
label,
is_asm,
is_formatter,
}
},
)
.collect(),
})
.collect())
},
)
.await
.map_err(warp::reject::custom)?;
Ok(warp::reply::with_header(
warp::reply::json(&JsonLanguage {
mode,
mime,
shared_wrappers,
implementations,
}),
CACHE_CONTROL,
"max-age=14400",
))
.collect();
Ok(warp::reply::with_header(
warp::reply::json(&JsonLanguage {
mode,
mime,
shared_wrappers,
implementations,
}),
CACHE_CONTROL,
"max-age=14400",
))
})
.compat()
}

View File

@ -1,10 +1,10 @@
use crate::schema::languages;
use crate::PgPool;
use crate::Connection;
use diesel::prelude::*;
use futures::Future;
use futures03::TryFutureExt;
use serde::Serialize;
use tokio_diesel::AsyncRunQueryDsl;
use tokio_executor::blocking;
use warp::{Rejection, Reply};
#[derive(Queryable, Serialize)]
@ -13,11 +13,13 @@ struct Language {
name: String,
}
pub fn languages(pool: &'static PgPool) -> impl Future<Item = impl Reply, Error = Rejection> {
languages::table
.select((languages::identifier, languages::name))
.load_async(pool)
.compat()
.map(|languages: Vec<Language>| warp::reply::json(&languages))
.map_err(warp::reject::custom)
pub fn languages(connection: Connection) -> impl Future<Item = impl Reply, Error = Rejection> {
blocking::run(move || {
let languages: Vec<Language> = languages::table
.select((languages::identifier, languages::name))
.load(&connection)
.map_err(warp::reject::custom)?;
Ok(warp::reply::json(&languages))
})
.compat()
}

View File

@ -2,51 +2,45 @@ use crate::models::language::{Language, Selection};
use crate::models::paste::{ExternPaste, Paste};
use crate::schema::{languages, pastes};
use crate::templates::RenderRucte;
use crate::{templates, PgPool};
use crate::{templates, Connection};
use diesel::prelude::*;
use futures::future::*;
use futures03::TryFutureExt;
use tokio_diesel::{AsyncRunQueryDsl, OptionalExtension};
use tokio_executor::blocking;
use warp::http::Response;
use warp::{Rejection, Reply};
pub fn display_paste(
requested_identifier: String,
pool: &'static PgPool,
connection: Connection,
) -> impl Future<Item = impl Reply, Error = Rejection> {
Paste::delete_old(pool)
.and_then(move |()| Language::fetch(pool))
.and_then(move |languages| {
pastes::table
.inner_join(languages::table)
.select((
pastes::paste,
pastes::language_id,
pastes::delete_at,
languages::is_markdown,
))
.filter(pastes::identifier.eq(requested_identifier))
.get_result_async(pool)
.compat()
.then(|result| result.optional())
.map(|paste| {
paste
.ok_or_else(warp::reject::not_found)
.and_then(|paste: Paste| {
let selected_language = Some(paste.language_id);
Response::builder().html(|o| {
templates::display_paste(
o,
ExternPaste::from_paste(paste),
Selection {
languages,
selected_language,
},
)
})
})
})
blocking::run(move || {
Paste::delete_old(&connection)?;
let languages = Language::fetch(&connection)?;
let paste: Paste = pastes::table
.inner_join(languages::table)
.select((
pastes::paste,
pastes::language_id,
pastes::delete_at,
languages::is_markdown,
))
.filter(pastes::identifier.eq(requested_identifier))
.get_result(&connection)
.optional()
.map_err(warp::reject::custom)?
.ok_or_else(warp::reject::not_found)?;
let selected_language = Some(paste.language_id);
Response::builder().html(|o| {
templates::display_paste(
o,
ExternPaste::from_paste(paste),
Selection {
languages,
selected_language,
},
)
})
.map_err(warp::reject::custom)
.flatten()
})
.compat()
}

View File

@ -1,22 +1,24 @@
use crate::models::language::{Language, Selection};
use crate::templates::RenderRucte;
use crate::{templates, PgPool};
use crate::{templates, Connection};
use futures::Future;
use futures03::TryFutureExt;
use tokio_executor::blocking;
use warp::http::Response;
use warp::{Rejection, Reply};
pub fn index(pool: &'static PgPool) -> impl Future<Item = impl Reply, Error = Rejection> {
Language::fetch(pool)
.map_err(warp::reject::custom)
.and_then(|languages| {
Response::builder().html(|o| {
templates::index(
o,
Selection {
languages,
selected_language: None,
},
)
})
pub fn index(connection: Connection) -> impl Future<Item = impl Reply, Error = Rejection> {
blocking::run(move || {
let languages = Language::fetch(&connection)?;
Response::builder().html(|o| {
templates::index(
o,
Selection {
languages,
selected_language: None,
},
)
})
})
.compat()
}

View File

@ -1,5 +1,5 @@
use crate::schema::{languages, pastes};
use crate::PgPool;
use crate::Connection;
use chrono::{DateTime, Duration, Utc};
use diesel::prelude::*;
use futures::Future;
@ -7,7 +7,7 @@ use futures03::TryFutureExt;
use rand::prelude::*;
use serde::de::IgnoredAny;
use serde::Deserialize;
use tokio_diesel::AsyncRunQueryDsl;
use tokio_executor::blocking;
use warp::http::Uri;
use warp::{Rejection, Reply};
@ -31,40 +31,40 @@ struct NewPaste {
pub fn insert_paste(
form: PasteForm,
pool: &'static PgPool,
connection: Connection,
) -> impl Future<Item = impl Reply, Error = Rejection> {
let mut rng = thread_rng();
let identifier: String = (0..10)
.map(|_| char::from(*CHARACTERS.choose(&mut rng).expect("a random character")))
.collect();
let cloned_identifier = identifier.clone();
let PasteForm {
language,
code,
autodelete,
} = form;
let delete_at = autodelete.map(|_| Utc::now() + Duration::hours(24));
languages::table
.select(languages::language_id)
.filter(languages::identifier.eq(language))
.get_result_async(pool)
.compat()
.and_then(move |language_id| {
diesel::insert_into(pastes::table)
.values(NewPaste {
identifier,
delete_at,
language_id,
paste: code,
})
.execute_async(pool)
.compat()
})
.map_err(warp::reject::custom)
.and_then(move |_| {
blocking::run(move || {
let mut rng = thread_rng();
let identifier: String = (0..10)
.map(|_| char::from(*CHARACTERS.choose(&mut rng).expect("a random character")))
.collect();
let cloned_identifier = identifier.clone();
let PasteForm {
language,
code,
autodelete,
} = form;
let delete_at = autodelete.map(|_| Utc::now() + Duration::hours(24));
let language_id = languages::table
.select(languages::language_id)
.filter(languages::identifier.eq(language))
.get_result(&connection)
.map_err(warp::reject::custom)?;
diesel::insert_into(pastes::table)
.values(NewPaste {
identifier,
delete_at,
language_id,
paste: code,
})
.execute(&connection)
.map_err(warp::reject::custom)?;
Ok(warp::redirect(
format!("/{}", cloned_identifier)
.parse::<Uri>()
.map_err(warp::reject::custom)
})
.map(warp::redirect)
.map_err(warp::reject::custom)?,
))
})
.compat()
}

View File

@ -7,101 +7,95 @@ mod raw_paste;
mod run;
use crate::templates::{self, RenderRucte};
use crate::PgPool;
use crate::Connection;
use diesel::prelude::*;
use diesel::r2d2::{ConnectionManager, Pool};
use futures03::TryFutureExt;
use std::ffi::OsStr;
use std::path::PathBuf;
use tokio_executor::blocking;
use warp::http::header::{
HeaderMap, HeaderValue, CONTENT_SECURITY_POLICY, REFERRER_POLICY, X_FRAME_OPTIONS,
};
use warp::http::{Response, StatusCode};
use warp::{path, Filter, Rejection, Reply};
fn pool_route(
pool: &'static PgPool,
) -> impl Filter<Extract = (&'static PgPool,), Error = Rejection> + Copy {
warp::any().and_then(move || -> Result<_, Rejection> { Ok(pool) })
type PgPool = Pool<ConnectionManager<PgConnection>>;
fn connection(pool: PgPool) -> impl Filter<Extract = (Connection,), Error = Rejection> + Clone {
warp::any().and_then(move || {
let pool = pool.clone();
blocking::run(move || pool.get().map_err(warp::reject::custom)).compat()
})
}
fn index(pool: &'static PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
fn index(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::end()
.and(warp::get2())
.and(pool_route(pool))
.and(connection(pool))
.and_then(index::index)
}
fn display_paste(
pool: &'static PgPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
fn display_paste(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path::end())
.and(warp::get2())
.and(pool_route(pool))
.and(connection(pool))
.and_then(display_paste::display_paste)
}
fn raw_paste(pool: &'static PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
fn raw_paste(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
with_ext("txt")
.and(warp::get2())
.and(pool_route(pool))
.and(connection(pool))
.and_then(raw_paste::raw_paste)
}
fn insert_paste(
pool: &'static PgPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
fn insert_paste(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::end()
.and(warp::post2())
.and(warp::body::content_length_limit(1_000_000))
.and(warp::body::form())
.and(pool_route(pool))
.and(connection(pool))
.and_then(insert_paste::insert_paste)
}
fn api_language(
pool: &'static PgPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
fn api_language(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
path!("api" / "v0" / "language" / String)
.and(warp::path::end())
.and(warp::get2())
.and(pool_route(pool))
.and_then(|identifier, pool| {
Box::pin(api_language::api_language(identifier, pool)).compat()
})
.and(connection(pool))
.and_then(api_language::api_language)
}
fn shared_run(
pool: &'static PgPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
fn shared_run(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
path!("api" / "v0" / "run" / String / String)
.and(warp::path::end())
.and(warp::post2())
.and(warp::body::content_length_limit(1_000_000))
.and(warp::body::form())
.and(pool_route(pool))
.and(connection(pool))
.and_then(run::shared)
}
fn implementation_run(
pool: &'static PgPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
pool: PgPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
path!("api" / "v0" / "run" / String / String / String)
.and(warp::path::end())
.and(warp::post2())
.and(warp::body::content_length_limit(1_000_000))
.and(warp::body::form())
.and(pool_route(pool))
.and(connection(pool))
.and_then(run::implementation)
}
fn api_v1_languages(
pool: &'static PgPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Copy {
fn api_v1_languages(pool: PgPool) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
path!("api" / "v1")
.and(warp::path("languages"))
.and(warp::path::end())
.and(warp::get2())
.and(pool_route(pool))
.and(connection(pool))
.and_then(api_v1::languages::languages)
}
@ -115,7 +109,9 @@ fn favicon() -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
.and(warp::fs::file("static/favicon.ico"))
}
pub fn routes(pool: &'static PgPool) -> impl Filter<Extract = (impl Reply,), Error = Rejection> {
pub fn routes(
pool: Pool<ConnectionManager<PgConnection>>,
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> {
let mut headers = HeaderMap::new();
headers.insert(
CONTENT_SECURITY_POLICY,
@ -133,14 +129,14 @@ pub fn routes(pool: &'static PgPool) -> impl Filter<Extract = (impl Reply,), Err
);
headers.insert(X_FRAME_OPTIONS, HeaderValue::from_static("DENY"));
headers.insert(REFERRER_POLICY, HeaderValue::from_static("no-referrer"));
index(pool)
index(pool.clone())
.or(favicon())
.or(raw_paste(pool))
.or(display_paste(pool))
.or(insert_paste(pool))
.or(api_language(pool))
.or(api_v1_languages(pool))
.or(shared_run(pool))
.or(raw_paste(pool.clone()))
.or(display_paste(pool.clone()))
.or(insert_paste(pool.clone()))
.or(api_language(pool.clone()))
.or(api_v1_languages(pool.clone()))
.or(shared_run(pool.clone()))
.or(implementation_run(pool))
.or(static_dir())
.recover(not_found)
@ -174,7 +170,6 @@ fn not_found(rejection: Rejection) -> Result<impl Reply, Rejection> {
#[cfg(test)]
mod test {
use super::routes;
use crate::PgPool;
use diesel::r2d2::{ConnectionManager, CustomizeConnection, Pool};
use diesel::Connection;
use lazy_static::lazy_static;
@ -188,19 +183,17 @@ mod test {
use warp::Filter;
lazy_static! {
static ref POOL: PgPool = {
static ref ROUTES: BoxedFilter<(Response,)> = {
let pool = Pool::builder()
.connection_customizer(Box::new(ExecuteWithinTransaction))
.max_size(1)
.build(ConnectionManager::new(env::var("DATABASE_URL").expect(
"Setting DATABASE_URL environment variable required to run tests",
)))
.expect("Couldn't create a connection pool");
.expect("Couldn't create a connection connection");
diesel_migrations::run_pending_migrations(&pool.get().unwrap()).unwrap();
pool
routes(pool).map(Reply::into_response).boxed()
};
static ref ROUTES: BoxedFilter<(Response,)> =
routes(&POOL).map(Reply::into_response).boxed();
}
#[derive(Debug)]

View File

@ -1,26 +1,25 @@
use crate::models::paste::Paste;
use crate::schema::pastes::dsl::*;
use crate::PgPool;
use crate::Connection;
use diesel::prelude::*;
use futures::Future;
use futures03::TryFutureExt;
use tokio_diesel::{AsyncRunQueryDsl, OptionalExtension};
use tokio_executor::blocking;
use warp::Rejection;
pub fn raw_paste(
requested_identifier: String,
pool: &'static PgPool,
connection: Connection,
) -> impl Future<Item = String, Error = Rejection> {
Paste::delete_old(pool)
.and_then(move |()| {
pastes
.select(paste)
.filter(identifier.eq(requested_identifier))
.get_result_async(pool)
.compat()
.then(|result| result.optional())
.map(|paste_contents| paste_contents.ok_or_else(warp::reject::not_found))
})
.map_err(warp::reject::custom)
.flatten()
blocking::run(move || {
Paste::delete_old(&connection)?;
pastes
.select(paste)
.filter(identifier.eq(requested_identifier))
.get_result(&connection)
.optional()
.map_err(warp::reject::custom)?
.ok_or_else(warp::reject::not_found)
})
.compat()
}

View File

@ -1,5 +1,5 @@
use crate::schema::{implementation_wrappers, implementations, languages, shared_wrappers};
use crate::PgPool;
use crate::Connection;
use diesel::prelude::*;
use futures::Future;
use futures03::TryFutureExt;
@ -7,7 +7,7 @@ use lazy_static::lazy_static;
use reqwest::r#async::Client;
use serde::{Deserialize, Serialize};
use std::env;
use tokio_diesel::{AsyncRunQueryDsl, OptionalExtension};
use tokio_executor::blocking;
use warp::{Rejection, Reply};
lazy_static! {
@ -49,35 +49,36 @@ pub fn shared(
code,
compiler_options,
}: Form,
pool: &'static PgPool,
connection: Connection,
) -> impl Future<Item = impl Reply, Error = Rejection> {
languages::table
.inner_join(shared_wrappers::table)
.filter(languages::identifier.eq(language))
.filter(shared_wrappers::identifier.eq(identifier))
.select(shared_wrappers::code)
.get_result_async(pool)
.compat()
.then(|result| result.optional())
.map(|wrapper| wrapper.ok_or_else(warp::reject::not_found))
.map_err(warp::reject::custom)
.flatten()
.and_then(move |language_code: String| {
CLIENT
.post(SANDBOX_URL.as_str())
.json(&Request {
files: vec![File {
name: "code",
contents: code,
}],
stdin: "",
code: language_code.replace("%s", &compiler_options),
})
.send()
.and_then(|mut r| r.json())
.map_err(warp::reject::custom)
})
.map(|output: Output| warp::reply::json(&output))
blocking::run(move || {
languages::table
.inner_join(shared_wrappers::table)
.filter(languages::identifier.eq(language))
.filter(shared_wrappers::identifier.eq(identifier))
.select(shared_wrappers::code)
.get_result(&connection)
.optional()
.map_err(warp::reject::custom)?
.ok_or_else(warp::reject::not_found)
})
.compat()
.and_then(move |language_code: String| {
CLIENT
.post(SANDBOX_URL.as_str())
.json(&Request {
files: vec![File {
name: "code",
contents: code,
}],
stdin: "",
code: language_code.replace("%s", &compiler_options),
})
.send()
.and_then(|mut r| r.json())
.map_err(warp::reject::custom)
})
.map(|output: Output| warp::reply::json(&output))
}
pub fn implementation(
@ -88,35 +89,36 @@ pub fn implementation(
code,
compiler_options,
}: Form,
pool: &'static PgPool,
connection: Connection,
) -> impl Future<Item = impl Reply, Error = Rejection> {
implementations::table
.inner_join(implementation_wrappers::table)
.inner_join(languages::table)
.filter(languages::identifier.eq(language))
.filter(implementations::identifier.eq(implementation))
.filter(implementation_wrappers::identifier.eq(identifier))
.select(implementation_wrappers::code)
.get_result_async(pool)
.compat()
.then(|result| result.optional())
.map(|wrapper| wrapper.ok_or_else(warp::reject::not_found))
.map_err(warp::reject::custom)
.flatten()
.and_then(move |language_code: String| {
CLIENT
.post(SANDBOX_URL.as_str())
.json(&Request {
files: vec![File {
name: "code",
contents: code,
}],
stdin: "",
code: language_code.replace("%s", &compiler_options),
})
.send()
.and_then(|mut r| r.json())
.map_err(warp::reject::custom)
})
.map(|output: Output| warp::reply::json(&output))
blocking::run(move || {
implementations::table
.inner_join(implementation_wrappers::table)
.inner_join(languages::table)
.filter(languages::identifier.eq(language))
.filter(implementations::identifier.eq(implementation))
.filter(implementation_wrappers::identifier.eq(identifier))
.select(implementation_wrappers::code)
.get_result(&connection)
.optional()
.map_err(warp::reject::custom)?
.ok_or_else(warp::reject::not_found)
})
.compat()
.and_then(move |language_code: String| {
CLIENT
.post(SANDBOX_URL.as_str())
.json(&Request {
files: vec![File {
name: "code",
contents: code,
}],
stdin: "",
code: language_code.replace("%s", &compiler_options),
})
.send()
.and_then(|mut r| r.json())
.map_err(warp::reject::custom)
})
.map(|json: Output| warp::reply::json(&json))
}