Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
452 changes: 329 additions & 123 deletions frameworks/Rust/xitca-web/Cargo.lock

Large diffs are not rendered by default.

20 changes: 11 additions & 9 deletions frameworks/Rust/xitca-web/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "xitca-web"
version = "0.1.0"
edition = "2021"
edition = "2024"

[[bin]]
name = "xitca-web"
Expand Down Expand Up @@ -46,7 +46,7 @@ template = ["dep:sailfish"]
# io-uring optional
io-uring = ["dep:tokio-uring", "xitca-http/io-uring", "xitca-server/io-uring"]
# unrealistic performance optimization
perf = ["dep:core_affinity", "dep:mimalloc", "tokio/parking_lot"]
perf = ["dep:core_affinity", "dep:mimalloc", "tokio/parking_lot", "simd-json", "simd-json-derive"]

[dependencies]
xitca-http = "0.7"
Expand Down Expand Up @@ -83,11 +83,13 @@ tokio-uring = { version = "0.5", optional = true }
# perf optional
core_affinity = { version = "0.8.1", optional = true }
mimalloc = { version = "0.1", default-features = false, optional = true }
simd-json = { version = "0.14", optional = true }
simd-json-derive = { version = "0.15", default-features = false, optional = true }

# stuff can not be used or not needed in wasi target
[target.'cfg(not(target_family = "wasm"))'.dependencies]
futures-core = { version = "0.3", default-features = false }
rand = { version = "0.8", features = ["small_rng"] }
rand = { version = "0.9", features = ["os_rng", "small_rng"], default-features = false }
tokio = "1.41"

[profile.release]
Expand All @@ -102,9 +104,9 @@ xitca-postgres-diesel = { git = "https://github.com/fakeshadow/xitca-postgres-di
diesel-async = { git = "https://github.com/weiznich/diesel_async", rev = "5b8262b" }
mio = { git = "https://github.com/fakeshadow/mio", rev = "9bae6012b7ecfc6083350785f71a5e8265358178" }

xitca-codegen = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" }
xitca-http = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" }
xitca-postgres = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" }
xitca-server = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" }
xitca-service = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" }
xitca-web = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" }
xitca-codegen = { git = "http://github.com/HFQR/xitca-web", rev = "c2de532" }
xitca-http = { git = "http://github.com/HFQR/xitca-web", rev = "c2de532" }
xitca-postgres = { git = "http://github.com/HFQR/xitca-web", rev = "c2de532" }
xitca-server = { git = "http://github.com/HFQR/xitca-web", rev = "c2de532" }
xitca-service = { git = "http://github.com/HFQR/xitca-web", rev = "c2de532" }
xitca-web = { git = "http://github.com/HFQR/xitca-web", rev = "c2de532" }
6 changes: 3 additions & 3 deletions frameworks/Rust/xitca-web/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ mod db_util;

use core::cell::RefCell;

use xitca_postgres::{iter::AsyncLendingIterator, pipeline::Pipeline, pool::Pool, statement::Statement, Execute};
use xitca_postgres::{Execute, iter::AsyncLendingIterator, pipeline::Pipeline, pool::Pool, statement::Statement};

use super::{
ser::{Fortune, Fortunes, World},
util::{HandleResult, DB_URL},
util::{DB_URL, HandleResult},
};

use db_util::{not_found, sort_update_params, update_query_from_num, Shared, FORTUNE_STMT, WORLD_STMT};
use db_util::{FORTUNE_STMT, Shared, WORLD_STMT, not_found, sort_update_params, update_query_from_num};

pub struct Client {
pool: Pool,
Expand Down
2 changes: 1 addition & 1 deletion frameworks/Rust/xitca-web/src/db_diesel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use diesel::{prelude::*, r2d2};

use crate::{
ser::{Fortune, Fortunes, World},
util::{HandleResult, Rand, DB_URL},
util::{DB_URL, HandleResult, Rand},
};

use db_util::{not_found, update_query_from_ids};
Expand Down
4 changes: 2 additions & 2 deletions frameworks/Rust/xitca-web/src/db_diesel_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use std::{io, sync::Mutex};

use diesel::prelude::*;
use diesel_async::{
pooled_connection::{bb8, AsyncDieselConnectionManager},
RunQueryDsl,
pooled_connection::{AsyncDieselConnectionManager, bb8},
};
use futures_util::{
future::join,
Expand All @@ -16,7 +16,7 @@ use xitca_postgres_diesel::AsyncPgConnection;

use crate::{
ser::{Fortune, Fortunes, World},
util::{HandleResult, Rand, DB_URL},
util::{DB_URL, HandleResult, Rand},
};

use db_util::{not_found, update_query_from_ids};
Expand Down
26 changes: 12 additions & 14 deletions frameworks/Rust/xitca-web/src/db_unrealistic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ mod db_util;

use std::cell::RefCell;

use xitca_postgres::{iter::AsyncLendingIterator, pipeline::Pipeline, statement::Statement, Execute};
use xitca_postgres::{Execute, iter::AsyncLendingIterator, pipeline::Pipeline, statement::Statement};

use super::{
ser::{Fortune, Fortunes, World},
util::{HandleResult, DB_URL},
util::{DB_URL, HandleResult},
};

use db_util::{not_found, sort_update_params, update_query_from_num, Shared, FORTUNE_STMT, WORLD_STMT};
use db_util::{FORTUNE_STMT, Shared, WORLD_STMT, not_found, sort_update_params, update_query_from_num};

pub struct Client {
cli: xitca_postgres::Client,
Expand All @@ -36,7 +36,7 @@ pub async fn create() -> HandleResult<Client> {

let mut updates = vec![Statement::default()];

for update in (1..=500).map(update_query_from_num).into_iter() {
for update in (1..=500).map(update_query_from_num) {
let stmt = Statement::named(&update, &[]).execute(&cli).await?.leak();
updates.push(stmt);
}
Expand All @@ -61,20 +61,18 @@ impl Client {
pub async fn get_worlds(&self, num: u16) -> HandleResult<Vec<World>> {
let len = num as usize;

let mut res = Vec::with_capacity(len);

{
let (ref mut rng, ..) = *self.shared.borrow_mut();
for _ in 0..len {
let stream = self.world.bind([rng.gen_id()]).query(&self.cli).await?;
res.push(stream);
}
let mut res = {
let (ref mut rng, ref mut buf) = *self.shared.borrow_mut();
// unrealistic as all queries are sent with only one sync point.
let mut pipe = Pipeline::unsync_with_capacity_from_buf(len, buf);
(0..num).try_for_each(|_| self.world.bind([rng.gen_id()]).query(&mut pipe))?;
pipe.query(&self.cli)?
};

let mut worlds = Vec::with_capacity(len);

for mut stream in res {
let row = stream.try_next().await?.ok_or_else(not_found)?;
while let Some(mut item) = res.try_next().await? {
let row = item.try_next().await?.ok_or_else(not_found)?;
worlds.push(World::new(row.get(0), row.get(1)));
}

Expand Down
30 changes: 12 additions & 18 deletions frameworks/Rust/xitca-web/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ mod ser;
mod util;

use xitca_http::{
HttpServiceBuilder,
h1::RequestBody,
http::{header::SERVER, StatusCode},
http::{StatusCode, header::SERVER},
util::{
middleware::context::{Context, ContextBuilder},
service::{
route::get,
router::{Router, RouterError},
},
},
HttpServiceBuilder,
};
use xitca_service::{fn_service, Service, ServiceExt};
use xitca_service::{Service, ServiceExt, fn_service};

use db::Client;
use ser::{error_response, IntoResponse, Message, Request, Response};
use util::{HandleResult, QueryParse, State, SERVER_HEADER_VALUE};
use ser::{IntoResponse, Message, Request, Response, error_response};
use util::{HandleResult, QueryParse, SERVER_HEADER_VALUE, State};

type Ctx<'a> = Context<'a, Request<RequestBody>, State<Client>>;

Expand All @@ -30,33 +30,27 @@ fn main() -> std::io::Result<()> {
.insert("/fortunes", get(fn_service(fortunes)))
.insert("/queries", get(fn_service(queries)))
.insert("/updates", get(fn_service(updates)))
.enclosed_fn(middleware)
.enclosed(ContextBuilder::new(|| async { db::create().await.map(State::new) }))
.enclosed_fn(async |service, req| {
let mut res = service.call(req).await.unwrap_or_else(error_handler);
res.headers_mut().insert(SERVER, SERVER_HEADER_VALUE);
Ok::<_, core::convert::Infallible>(res)
})
.enclosed(HttpServiceBuilder::h1().io_uring());
xitca_server::Builder::new()
.bind("xitca-web", "0.0.0.0:8080", service)?
.build()
.wait()
}

async fn middleware<S>(service: &S, req: Ctx<'_>) -> Result<Response, core::convert::Infallible>
where
S: for<'c> Service<Ctx<'c>, Response = Response, Error = RouterError<util::Error>>,
{
let mut res = service.call(req).await.unwrap_or_else(error_handler);
res.headers_mut().insert(SERVER, SERVER_HEADER_VALUE);
Ok(res)
}

#[cold]
#[inline(never)]
fn error_handler(e: RouterError<util::Error>) -> Response {
let status = match e {
error_response(match e {
RouterError::Match(_) => StatusCode::NOT_FOUND,
RouterError::NotAllowed(_) => StatusCode::METHOD_NOT_ALLOWED,
RouterError::Service(_) => StatusCode::INTERNAL_SERVER_ERROR,
};
error_response(status)
})
}

async fn plain_text(ctx: Ctx<'_>) -> HandleResult<Response> {
Expand Down
19 changes: 6 additions & 13 deletions frameworks/Rust/xitca-web/src/main_orm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ mod schema;
mod ser;
mod util;

use serde::Serialize;
use xitca_web::{
App,
codegen::route,
handler::{html::Html, json::Json, query::Query, state::StateRef, text::Text},
http::{header::SERVER, WebResponse},
http::{WebResponse, header::SERVER},
route::get,
App,
};

use db_diesel_async::Pool;
use ser::Num;
use ser::{Num, World};
use util::{HandleResult, SERVER_HEADER_VALUE};

fn main() -> std::io::Result<()> {
Expand All @@ -39,7 +38,7 @@ fn header(mut res: WebResponse) -> WebResponse {
}

#[route("/db", method = get)]
async fn db(StateRef(pool): StateRef<'_, Pool>) -> HandleResult<Json<impl Serialize>> {
async fn db(StateRef(pool): StateRef<'_, Pool>) -> HandleResult<Json<World>> {
pool.get_world().await.map(Json)
}

Expand All @@ -51,17 +50,11 @@ async fn fortunes(StateRef(pool): StateRef<'_, Pool>) -> HandleResult<Html<Strin
}

#[route("/queries", method = get)]
async fn queries(
Query(Num(num)): Query<Num>,
StateRef(pool): StateRef<'_, Pool>,
) -> HandleResult<Json<impl Serialize>> {
async fn queries(Query(Num(num)): Query<Num>, StateRef(pool): StateRef<'_, Pool>) -> HandleResult<Json<Vec<World>>> {
pool.get_worlds(num).await.map(Json)
}

#[route("/updates", method = get)]
async fn updates(
Query(Num(num)): Query<Num>,
StateRef(pool): StateRef<'_, Pool>,
) -> HandleResult<Json<impl Serialize>> {
async fn updates(Query(Num(num)): Query<Num>, StateRef(pool): StateRef<'_, Pool>) -> HandleResult<Json<Vec<World>>> {
pool.update(num).await.map(Json)
}
4 changes: 2 additions & 2 deletions frameworks/Rust/xitca-web/src/main_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ mod util;

use serde::Serialize;
use xitca_web::{
App,
codegen::route,
handler::{html::Html, json::Json, query::Query, state::StateOwn, text::Text},
http::{header::SERVER, WebResponse},
http::{WebResponse, header::SERVER},
route::get,
App,
};

use db_diesel::Pool;
Expand Down
9 changes: 6 additions & 3 deletions frameworks/Rust/xitca-web/src/main_unrealistic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ use xitca_http::{
http::StatusCode,
};
use xitca_service::Service;
// simd-json crate is realistic approach to json serializer.
// That said xitca-web by default utilize serde-json as serializer making it an unrealistic representation of framework performance
use simd_json_derive::Serialize;

use self::{
ser::Message,
Expand Down Expand Up @@ -106,7 +109,7 @@ async fn handler<'h>(req: Request<'h, State<db::Client>>, res: Response<'h>) ->
.header("server", "X")
// unrealistic content length header.
.header("content-length", "27")
.body_writer(|buf| serde_json::to_writer(BufMutWriter(buf), &Message::new()).unwrap()),
.body_writer(|buf| Message::new().json_write(&mut BufMutWriter(buf)).unwrap()),

// all database related categories are unrealistic. please reference db_unrealistic module for detail.
"/fortunes" => {
Expand Down Expand Up @@ -139,10 +142,10 @@ async fn handler<'h>(req: Request<'h, State<db::Client>>, res: Response<'h>) ->

fn json_response<'r, DB, T>(res: Response<'r>, state: &State<DB>, val: &T) -> Response<'r, 3>
where
T: serde::Serialize,
T: Serialize,
{
let buf = &mut *state.write_buf.borrow_mut();
serde_json::to_writer(BufMutWriter(buf), val).unwrap();
val.json_write(&mut BufMutWriter(buf)).unwrap();
let res = res
.status(StatusCode::OK)
.header("content-type", "application/json")
Expand Down
8 changes: 5 additions & 3 deletions frameworks/Rust/xitca-web/src/main_wasm.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
mod ser;
mod util;

use std::os::wasi::io::FromRawFd;

use xitca_web::{
App,
handler::{json::Json, text::Text},
http::{header::SERVER, WebResponse},
http::{WebResponse, header::SERVER},
route::get,
App,
};

fn main() -> std::io::Result<()> {
let listener = std::env::var("FD_COUNT")
.ok()
.and_then(|v| v.parse().ok())
.map(|fd| unsafe { std::os::wasi::io::FromRawFd::from_raw_fd(fd) })
.map(|fd| unsafe { std::net::TcpListener::from_raw_fd(fd) })
.expect("failed to parse FD_COUNT env");

App::new()
Expand Down
9 changes: 5 additions & 4 deletions frameworks/Rust/xitca-web/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

use std::borrow::Cow;

use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::SerializeStruct};
use xitca_http::{
body::Once,
bytes::{BufMutWriter, Bytes},
http::{
self,
self, IntoResponse as _, RequestExt, StatusCode,
const_header_value::{JSON, TEXT_HTML_UTF8, TEXT_UTF8},
header::CONTENT_TYPE,
IntoResponse as _, RequestExt, StatusCode,
},
};

Expand All @@ -19,6 +18,7 @@ use crate::util::{Error, State};
const HELLO: &str = "Hello, World!";
const HELLO_BYTES: &[u8] = HELLO.as_bytes();

#[cfg_attr(feature = "perf", derive(simd_json_derive::Serialize))]
#[derive(Clone)]
pub struct Message {
message: &'static str,
Expand All @@ -34,6 +34,7 @@ impl Message {
pub struct Num(pub u16);

#[cfg_attr(any(feature = "pg-orm", feature = "pg-orm-async"), derive(diesel::Queryable))]
#[cfg_attr(feature = "perf", derive(simd_json_derive::Serialize))]
pub struct World {
pub id: i32,
pub randomnumber: i32,
Expand Down Expand Up @@ -124,7 +125,7 @@ impl<'de> Deserialize<'de> for Num {
{
struct FieldVisitor;

impl<'de> Visitor<'de> for FieldVisitor {
impl Visitor<'_> for FieldVisitor {
type Value = Field;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
Expand Down
Loading
Loading