feat(server): use msgpack instead of json

This commit is contained in:
Anna 2023-10-06 01:37:51 -04:00
parent 28746242d3
commit 71ecba455f
Signed by: anna
GPG Key ID: D0943384CD9F87D1
3 changed files with 144 additions and 16 deletions

25
server/Cargo.lock generated
View File

@ -1043,8 +1043,11 @@ version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"bytes",
"chrono",
"rmp-serde",
"serde",
"serde_path_to_error",
"sqlx",
"tokio",
"tower",
@ -1114,6 +1117,28 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "rmp"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20"
dependencies = [
"byteorder",
"num-traits",
"paste",
]
[[package]]
name = "rmp-serde"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a"
dependencies = [
"byteorder",
"rmp",
"serde",
]
[[package]]
name = "rsa"
version = "0.9.2"

View File

@ -8,8 +8,11 @@ edition = "2021"
[dependencies]
anyhow = "1"
axum = "0.6"
bytes = "1"
chrono = { version = "0.4", features = ["serde"] }
rmp-serde = "1"
serde = { version = "1", features = ["derive"] }
serde_path_to_error = "0.1"
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite", "chrono"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
tower = "0.4"

View File

@ -1,13 +1,18 @@
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use anyhow::Result;
use axum::{BoxError, Json, Router, Server};
use axum::{async_trait, BoxError, Router, Server};
use axum::body::HttpBody;
use axum::error_handling::HandleErrorLayer;
use axum::extract::{Path, State};
use axum::http::{Method, StatusCode};
use axum::extract::{FromRequest, Path, State};
use axum::extract::rejection::BytesRejection;
use axum::http::{HeaderValue, Request, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::routing::{get, post};
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use serde::de::DeserializeOwned;
use sqlx::SqlitePool;
use tower::ServiceBuilder;
use tower_http::compression::CompressionLayer;
@ -21,14 +26,17 @@ async fn main() -> Result<()> {
let pool = SqlitePool::connect("./database.sqlite").await?;
MIGRATOR.run(&pool).await?;
let cors = CorsLayer::new()
#[cfg(not(debug_assertions))]
let cors = CorsLayer::new()
.allow_origin([
"https://map.anna.lgbt".parse()?,
])
.allow_methods([Method::GET]);
#[cfg(debug_assertions)]
let cors = CorsLayer::permissive();
let app = Router::new()
.route("/", get(index))
.route("/:territory", get(territory))
.route("/:territory/:world", get(territory_world))
.route("/upload", post(upload))
@ -49,14 +57,10 @@ async fn main() -> Result<()> {
Ok(())
}
async fn index() -> &'static str {
"hi"
}
async fn territory(
State(pool): State<Arc<SqlitePool>>,
Path(territory): Path<u32>,
) -> Result<Json<Vec<AnonymousPlayerInfo>>, AppError>
) -> Result<MsgPack<Vec<AnonymousPlayerInfo>>, AppError>
{
let info = sqlx::query_as!(
AnonymousPlayerInfo,
@ -75,20 +79,20 @@ async fn territory(
max_hp
from players
where territory = ?
and unixepoch('now') - unixepoch(timestamp) < 30
--and unixepoch('now') - unixepoch(timestamp) < 30
"#,
territory,
)
.fetch_all(&*pool)
.await?;
Ok(Json(info))
Ok(MsgPack(info))
}
async fn territory_world(
State(pool): State<Arc<SqlitePool>>,
Path((territory, world)): Path<(u32, u32)>,
) -> Result<Json<Vec<AnonymousPlayerInfo>>, AppError>
) -> Result<MsgPack<Vec<AnonymousPlayerInfo>>, AppError>
{
let info = sqlx::query_as!(
AnonymousPlayerInfo,
@ -108,7 +112,7 @@ async fn territory_world(
from players
where territory = ?
and current_world = ?
and unixepoch('now') - unixepoch(timestamp) < 30
--and unixepoch('now') - unixepoch(timestamp) < 30
"#,
territory,
world,
@ -116,12 +120,12 @@ async fn territory_world(
.fetch_all(&*pool)
.await?;
Ok(Json(info))
Ok(MsgPack(info))
}
async fn upload(
pool: State<Arc<SqlitePool>>,
data: Json<Update>,
data: MsgPack<Update>,
) -> Result<(), AppError> {
let mut t = pool.begin().await?;
@ -272,3 +276,99 @@ impl<E> From<E> for AppError
Self(err.into())
}
}
struct MsgPack<T>(pub T);
impl<T> Deref for MsgPack<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for MsgPack<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[async_trait]
impl<S, B, T> FromRequest<S, B> for MsgPack<T>
where S: Send + Sync,
B: HttpBody + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
T: DeserializeOwned,
{
type Rejection = MsgPackRejection;
async fn from_request(req: Request<B>, state: &S) -> std::result::Result<Self, Self::Rejection> {
if req.headers().get("content-type").and_then(|val| val.to_str().ok()) != Some("application/msgpack") {
return Err(MsgPackRejection::ContentType);
}
let bytes = Bytes::from_request(req, state).await?;
let value = rmp_serde::from_slice(&bytes)?;
// let des = &mut rmp_serde::Deserializer::from_read_ref(&bytes);
// let value = match serde_path_to_error::deserialize(des) {
// Ok(v) => v,
// Err(err) => {
// let rejection = match err.inner() {
// rmp_serde::decode::Error::DepthLimitExceeded => {},
// };
//
// return Err(rejection);
// }
// };
Ok(MsgPack(value))
}
}
impl<T> IntoResponse for MsgPack<T>
where T: Serialize,
{
fn into_response(self) -> Response {
(
[(
axum::http::header::CONTENT_TYPE,
HeaderValue::from_static("application/msgpack"),
)],
rmp_serde::to_vec(&self.0)
.map_err(|e| AppError(e.into()))
.into_response()
).into_response()
}
}
enum MsgPackRejection {
ContentType,
Bytes(BytesRejection),
MsgPack(rmp_serde::decode::Error),
}
impl IntoResponse for MsgPackRejection {
fn into_response(self) -> Response {
(
StatusCode::BAD_REQUEST,
match self {
Self::ContentType => "expected application/msgpack content-type header".into_response(),
Self::Bytes(e) => e.into_response(),
Self::MsgPack(e) => format!("could not deserialize msgpack: {:#}", e).into_response(),
}
).into_response()
}
}
impl From<BytesRejection> for MsgPackRejection {
fn from(value: BytesRejection) -> Self {
Self::Bytes(value)
}
}
impl From<rmp_serde::decode::Error> for MsgPackRejection {
fn from(value: rmp_serde::decode::Error) -> Self {
Self::MsgPack(value)
}
}