clone-session-service-objects
Avril 4 years ago
parent a277f3fd9d
commit 5b4fa57a9c
Signed by: flanchan
GPG Key ID: 284488987C31F630

195
Cargo.lock generated

@ -64,6 +64,12 @@ dependencies = [
"safemem",
]
[[package]]
name = "build_const"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
[[package]]
name = "byte-tools"
version = "0.3.1"
@ -82,6 +88,12 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
[[package]]
name = "cc"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
[[package]]
name = "cfg-if"
version = "0.1.10"
@ -109,6 +121,45 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
[[package]]
name = "crc"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
dependencies = [
"build_const",
]
[[package]]
name = "crypto-mac"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca"
dependencies = [
"generic-array 0.14.4",
"subtle",
]
[[package]]
name = "cryptohelpers"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "705d37f9525e6e677af724ec414c7cb712e8576dbdd11bafbcfbcea2f468bcd9"
dependencies = [
"crc",
"futures",
"getrandom 0.1.16",
"hex-literal",
"hmac",
"libc",
"openssl",
"pbkdf2",
"serde",
"serde_derive",
"sha2",
"tokio",
]
[[package]]
name = "difference"
version = "2.0.0"
@ -151,6 +202,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.0.0"
@ -278,6 +344,15 @@ dependencies = [
"slab",
]
[[package]]
name = "generational-arena"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601"
dependencies = [
"cfg-if 0.1.10",
]
[[package]]
name = "generic-array"
version = "0.12.3"
@ -308,6 +383,17 @@ dependencies = [
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4060f4657be78b8e766215b02b18a2e862d83745545de804638e2b545e81aee6"
dependencies = [
"cfg-if 1.0.0",
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
]
[[package]]
name = "h2"
version = "0.2.7"
@ -368,6 +454,22 @@ dependencies = [
"libc",
]
[[package]]
name = "hex-literal"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5af1f635ef1bc545d78392b136bfe1c9809e029023c84a3638a864a10b8819c8"
[[package]]
name = "hmac"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff"
dependencies = [
"crypto-mac",
"digest 0.9.0",
]
[[package]]
name = "http"
version = "0.2.2"
@ -650,6 +752,48 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
"foreign-types",
"lazy_static",
"libc",
"openssl-sys",
]
[[package]]
name = "openssl-sys"
version = "0.9.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6"
dependencies = [
"autocfg 1.0.1",
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "pbkdf2"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170d73bf11f39b4ce1809aabc95bf5c33564cdc16fc3200ddda17a5f6e5e48b"
dependencies = [
"base64",
"crypto-mac",
"hmac",
"rand 0.7.3",
"rand_core 0.5.1",
"sha2",
"subtle",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
@ -714,6 +858,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "ppv-lite86"
version = "0.2.10"
@ -781,7 +931,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
@ -829,7 +979,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
"getrandom 0.1.16",
]
[[package]]
@ -1013,6 +1163,19 @@ dependencies = [
"opaque-debug 0.3.0",
]
[[package]]
name = "sha2"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8"
dependencies = [
"block-buffer 0.9.0",
"cfg-if 1.0.0",
"cpuid-bool",
"digest 0.9.0",
"opaque-debug 0.3.0",
]
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
@ -1048,6 +1211,12 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "subtle"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
[[package]]
name = "syn"
version = "1.0.56"
@ -1295,6 +1464,22 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
[[package]]
name = "uuid"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
dependencies = [
"rand 0.7.3",
"serde",
]
[[package]]
name = "vcpkg"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
[[package]]
name = "version_check"
version = "0.9.2"
@ -1399,11 +1584,17 @@ dependencies = [
name = "yuurei"
version = "0.1.0"
dependencies = [
"cryptohelpers",
"difference",
"futures",
"generational-arena",
"getrandom 0.2.1",
"lazy_static",
"once_cell",
"serde",
"sha2",
"smallvec",
"tokio",
"uuid",
"warp",
]

@ -11,10 +11,16 @@ default = ["nightly"]
nightly = ["smallvec/const_generics"]
[dependencies]
cryptohelpers = {version = "1.7.1", features=["full"]}
difference = "2.0.0"
futures = "0.3.8"
generational-arena = "0.2.8"
getrandom = "0.2.1"
lazy_static = "1.4.0"
once_cell = "1.5.2"
serde = {version = "1.0.118", features=["derive"]}
sha2 = "0.9.2"
smallvec = {version = "1.6.0", features= ["union", "serde", "write"]}
tokio = {version = "0.2", features=["full"] }
uuid = {version = "0.8.1", features=["v4","serde"]}
warp = "0.2.5"

@ -0,0 +1,29 @@
use std::ptr;
/// Copy slice of bytes only
///
/// # Notes
/// `dst` and `src` must not overlap. See [move_slice].
pub fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize
{
let sz = std::cmp::min(dst.len(),src.len());
unsafe {
//libc::memcpy(&mut dst[0] as *mut u8 as *mut c_void, &src[0] as *const u8 as *const c_void, sz);
ptr::copy_nonoverlapping(&src[0] as *const u8, &mut dst[0] as *mut u8, sz);
}
sz
}
/// Move slice of bytes only
///
/// # Notes
/// `dst` and `src` can overlap.
pub fn move_slice(dst: &mut [u8], src: &[u8]) -> usize
{
let sz = std::cmp::min(dst.len(),src.len());
unsafe {
//libc::memmove(&mut dst[0] as *mut u8 as *mut c_void, &src[0] as *const u8 as *const c_void, sz);
ptr::copy(&src[0] as *const u8, &mut dst[0] as *mut u8, sz);
}
sz
}

@ -348,3 +348,144 @@ mod tests
}
}
}
#[macro_export] macro_rules! id_type {
($name:ident $(; $doc:literal)?) => {
$(#[doc(comment=$doc)])?
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ::serde::Serialize, ::serde::Deserialize)]
pub struct $name(uuid::Uuid);
impl $name
{
/// Create a random new unique ID
#[inline] fn id_new() -> Self
{
Self(::uuid::Uuid::new_v4())
}
/// Create from a UUID
#[inline] fn id_from(from: ::uuid::Uuid) -> Self
{
Self(from)
}
}
};
}
mod global_counter
{
use std::sync::atomic::{
Ordering, AtomicU64,
};
#[derive(Debug)]
pub struct GlobalCounter(AtomicU64);
impl GlobalCounter
{
/// Get and increment the counter
pub fn get(&self) -> u64
{
self.0.fetch_add(1, Ordering::SeqCst)
}
/// Check if this `u64` is valid to have come from this counter.
pub fn valid(&self, val: u64) -> bool
{
val <= self.0.load(Ordering::Acquire)
}
/// Create a new global counter.
pub const fn new() -> Self
{
Self(AtomicU64::new(0))
}
}
}
pub use global_counter::GlobalCounter;
const GLOBAL_SALT_SIZE: usize = 16;
lazy_static! {
pub static ref GLOBAL_SALT: &'static [u8] = {
let mut this = Box::new([0u8; GLOBAL_SALT_SIZE]);
getrandom::getrandom(&mut this[..]).expect("Failed to populate global salt");
&Box::leak(this)[..]
};
}
/// A wrapper for hashing with a specific salt.
#[derive(Debug, Hash)]
pub struct Salted<'a, T: std::hash::Hash>(&'a T, &'a [u8]);
impl<'a, T> Salted<'a, T>
where T: std::hash::Hash
{
/// Create a new wrapper.
pub fn new(val: &'a T, salt: &'a [u8]) -> Self
{
Self(val, &salt)
}
}
/// A wrapper for hashing with the global salt.
#[derive(Debug, Hash)]
pub struct GloballySalted<'a, T: std::hash::Hash>(&'a T, &'static [u8]);
impl<'a, T> GloballySalted<'a, T>
where T: std::hash::Hash
{
/// Create a new wrapper.
pub fn new(val: &'a T) -> Self
{
Self(val, &GLOBAL_SALT[..])
}
}
mod sha256_hasher {
use std::mem::size_of;
use sha2::{
Digest, Sha256,
};
use std::hash::{
Hasher, Hash,
};
use cryptohelpers::sha256::Sha256Hash;
struct Sha256Hasher(Sha256);
impl Sha256Hasher
{
pub fn new() -> Self
{
Self(Sha256::new())
}
}
impl Hasher for Sha256Hasher
{
fn write(&mut self, bytes: &[u8])
{
self.0.update(bytes);
}
fn finish(&self) -> u64
{
let ar = self.0.clone().finalize();
let mut rest = [0u8; size_of::<u64>()];
crate::bytes::move_slice(&mut rest[..], &ar[..]);
u64::from_le_bytes(rest)
}
}
pub trait Sha256HashExt
{
fn compute_sha256_hash(&self) -> Sha256Hash;
}
impl<T> Sha256HashExt for T
where T: Hash
{
fn compute_sha256_hash(&self) -> Sha256Hash {
let mut hasher = Sha256Hasher::new();
self.hash(&mut hasher);
hasher.0.into()
}
}
}
pub use sha256_hasher::Sha256HashExt;
/// Value may hold one in place or allocate on the heap to hold many.
pub type MaybeVec<T> = smallvec::SmallVec<[T; 1]>;

@ -4,9 +4,13 @@
#[cfg(all(feature="nightly", test))] extern crate test;
#[macro_use] extern crate serde;
#[macro_use] extern crate lazy_static;
use std::convert::{TryFrom, TryInto};
#[macro_use] mod ext; use ext::*;
mod bytes;
mod delta;
mod state;

@ -0,0 +1,153 @@
//! Creating immutable images of state.
use super::*;
use std::{error,fmt};
/// An image of the entire post container
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Imouto
{
posts: Vec<post::Post>,
}
impl From<Imouto> for Oneesan
{
#[inline] fn from(from: Imouto) -> Self
{
Self::from_freeze(from)
}
}
impl TryFrom<Oneesan> for Imouto
{
type Error = FreezeError;
#[inline] fn try_from(from: Oneesan) -> Result<Self, Self::Error>
{
from.try_into_freeze()
}
}
/// Error returned when a freeze operation fails
#[derive(Debug)]
pub struct FreezeError{
held: Option<Arc<RwLock<post::Post>>>,
}
impl FreezeError
{
/// The post associated with this error, if there is one.
pub fn post(&self) -> Option<&Arc<RwLock<post::Post>>>
{
self.held.as_ref()
}
}
impl error::Error for FreezeError{}
impl fmt::Display for FreezeError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f,"Failed to create freeze image")?;
if let Some(aref) = &self.held
{
let cnt = Arc::strong_count(&aref) - 1;
if cnt > 0 {
write!(f, "Post reference still held in {} other places.", cnt)
} else {
write!(f, "Post reference was still held in another place at the time, but no longer is.")
}
} else {
Ok(())
}
}
}
impl Oneesan
{
/// Create a serialisable image of this store by cloning each post into it.
pub async fn freeze(&self) -> Imouto
{
let read = self.posts.read().await;
let mut sis = Imouto{
posts: Vec::with_capacity(read.0.len()),
};
for (_, post) in read.0.iter()
{
sis.posts.push(post.read().await.clone());
}
sis
}
/// Consume into a serialisable image of this store.
///
/// # Fails
/// If references to any posts are still held elsewhere.
pub fn try_into_freeze(self) -> Result<Imouto, FreezeError>
{
let read = self.posts.into_inner();
let mut sis = Imouto{
posts: Vec::with_capacity(read.0.len()),
};
for post in read.0.into_iter()
{
sis.posts.push(match Arc::try_unwrap(post) {
Ok(val) => val.into_inner(),
Err(arc) => return Err(FreezeError{held: Some(arc)}),
// Err(_arc) => panic!("Reference to post is still being used"),//arc.read().await.clone(), // something else holds the reference, we can't consume it.
});
}
Ok(sis)
}
/// Consume into a serialisable image of this store.
///
/// # Panics
/// If references to any posts are still held elsewhere.
pub fn into_freeze(self) -> Imouto
{
self.try_into_freeze().expect("Failed to consume into freeze")
}
/// Create a new store from a serialisable image of one by cloning each post in it
pub fn unfreeze(freeze: &Imouto) -> Self
{
let mut posts = Arena::new();
let mut user_map = HashMap::new();
for post in freeze.posts.iter()
{
let idx = posts.insert(Arc::new(RwLock::new(post.clone())));
user_map.entry(post.owner().clone())
.or_insert_with(|| MaybeVec::new())
.push(idx);
}
Self {
posts: RwLock::new((posts, user_map))
}
}
/// Create a new store by consuming serialisable image of one by cloning each post in it
pub fn from_freeze(freeze: Imouto) -> Self
{
let mut posts = Arena::new();
let mut user_map = HashMap::new();
for post in freeze.posts.into_iter()
{
let mapping = user_map.entry(post.owner().clone())
.or_insert_with(|| MaybeVec::new());
let idx = posts.insert(Arc::new(RwLock::new(post)));
mapping.push(idx);
}
Self {
posts: RwLock::new((posts, user_map))
}
}
}

@ -1,5 +1,22 @@
use super::*;
use std::collections::HashMap;
use generational_arena::{
Arena, Index as ArenaIndex,
};
use std::sync::Arc;
use tokio::sync::RwLock;
pub mod session;
pub mod user;
pub mod post;
pub mod body;
mod freeze; pub use freeze::*;
/// Entire post container
pub struct Oneesan
{
posts: RwLock<(Arena<Arc<RwLock<post::Post>>> // All posts
, HashMap<user::UserID, MaybeVec<ArenaIndex>> // Post lookup by user ID
)>,
}

@ -1,18 +1,18 @@
use super::*;
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OpenPost
{
}
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ClosedPost
{
}
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum PostKind
{
Open(OpenPost),
@ -20,9 +20,18 @@ pub enum PostKind
}
/// A post
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Post
{
owner: user::UserID,
kind: PostKind
}
impl Post
{
/// The ID of the owning user & session of this post.
pub fn owner(&self) -> &user::UserID
{
&self.owner
}
}

@ -0,0 +1,25 @@
//! Session for each connected user
use super::*;
id_type!(SessionID; "A unique session ID, not bound to a user.");
#[derive(Debug)]
pub struct Session
{
id: SessionID,
user: user::User,
}
impl Session
{
/// The randomly generated ID of this session, irrespective of the user of this session.
#[inline] pub fn session_id(&self) -> &SessionID
{
&self.id
}
/// The unique user ID of this session
pub fn user_id(&self) -> user::UserID
{
self.user.id_for_session(self)
}
}

@ -3,5 +3,59 @@
//! Mostly for determining if a poster owns a post.
use super::*;
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct UserID(()); //TODO: User ID. Maybe use IP + session ID hash?
use std::{
net::SocketAddr,
};
use cryptohelpers::sha256;
/// A user's unique ID.
///
/// This is composed by the user's address and their session ID.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct UserID(SocketAddr, session::SessionID);
static COUNTER: GlobalCounter = GlobalCounter::new();
impl UserID
{
/// Generate a token from this instance.
///
/// User tokens are deterministically generated and can be deterministically verified.
pub fn generate_token(&self) -> u64
{
let cnt = COUNTER.get();
let mut trunc = [0u8; std::mem::size_of::<u64>()];
let hash = GloballySalted::new(self).compute_sha256_hash();
bytes::move_slice(&mut trunc[..], hash.as_ref());
u64::from_le_bytes(trunc) ^ cnt
}
/// Validate a token for this instance created with `generate_token`.
pub fn validate_token(&self, val: u64) -> bool
{
let mut trunc = [0u8; std::mem::size_of::<u64>()];
let hash = GloballySalted::new(self).compute_sha256_hash();
bytes::move_slice(&mut trunc[..], hash.as_ref());
COUNTER.valid(u64::from_le_bytes(trunc) ^ val)
}
}
/// A user not bound to a session.
#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct User
{
addr: SocketAddr,
}
impl User
{
/// Get the user ID for this session.
pub fn id_for_session(&self, session: &session::Session) -> UserID
{
UserID(self.addr, session.session_id().clone())
}
}

Loading…
Cancel
Save