This commit is contained in:
John Smith
2022-07-10 17:36:50 -04:00
parent cd0cd78e30
commit 7e0d7dad06
108 changed files with 1378 additions and 1535 deletions
+5
View File
@@ -15,9 +15,12 @@ mod mutable_future;
mod single_shot_eventual;
mod split_url;
mod tick_task;
mod timeout_or;
mod tools;
pub use cfg_if::*;
#[allow(unused_imports)]
pub use eyre::{bail, ensure, eyre, Report as EyreReport, Result as EyreResult, WrapErr};
pub use futures_util::future::{select, Either};
pub use futures_util::select;
pub use futures_util::stream::FuturesUnordered;
@@ -28,6 +31,7 @@ pub use parking_lot::*;
pub use split_url::*;
pub use static_assertions::*;
pub use stop_token::*;
pub use thiserror::Error as ThisError;
pub use tracing::*;
pub type PinBox<T> = Pin<Box<T>>;
@@ -124,4 +128,5 @@ pub use mutable_future::*;
// pub use single_future::*;
pub use single_shot_eventual::*;
pub use tick_task::*;
pub use timeout_or::*;
pub use tools::*;
+32 -18
View File
@@ -7,7 +7,7 @@
// URLs must convert to UTF8
// Only IP address and DNS hostname host fields are supported
use super::{IpAddr, Ipv4Addr, Ipv6Addr};
use super::*;
use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::vec::Vec;
@@ -62,12 +62,14 @@ fn is_valid_scheme<H: AsRef<str>>(host: H) -> bool {
true
}
fn hex_decode(h: u8) -> Result<u8, String> {
fn hex_decode(h: u8) -> Result<u8, SplitUrlError> {
match h {
b'0'..=b'9' => Ok(h - b'0'),
b'A'..=b'F' => Ok(h - b'A' + 10),
b'a'..=b'f' => Ok(h - b'a' + 10),
_ => Err("Unexpected character in percent encoding".to_owned()),
_ => Err(SplitUrlError::new(
"Unexpected character in percent encoding",
)),
}
}
@@ -88,10 +90,10 @@ fn hex_encode(c: u8) -> (char, char) {
)
}
fn url_decode<S: AsRef<str>>(s: S) -> Result<String, String> {
fn url_decode<S: AsRef<str>>(s: S) -> Result<String, SplitUrlError> {
let url = s.as_ref().to_owned();
if !url.is_ascii() {
return Err("URL is not in ASCII encoding".to_owned());
return Err(SplitUrlError::new("URL is not in ASCII encoding"));
}
let url_bytes = url.as_bytes();
let mut dec_bytes: Vec<u8> = Vec::with_capacity(url_bytes.len());
@@ -102,14 +104,15 @@ fn url_decode<S: AsRef<str>>(s: S) -> Result<String, String> {
i += 1;
if b == b'%' {
if (i + 1) >= end {
return Err("Invalid URL encoding".to_owned());
return Err(SplitUrlError::new("Invalid URL encoding"));
}
b = hex_decode(url_bytes[i])? << 4 | hex_decode(url_bytes[i + 1])?;
i += 2;
}
dec_bytes.push(b);
}
String::from_utf8(dec_bytes).map_err(|e| format!("Decoded URL is not valid UTF-8: {}", e))
String::from_utf8(dec_bytes)
.map_err(|e| SplitUrlError::new(format!("Decoded URL is not valid UTF-8: {}", e)))
}
fn url_encode<S: AsRef<str>>(s: S, must_encode: impl Fn(u8) -> bool) -> String {
@@ -128,14 +131,25 @@ fn url_encode<S: AsRef<str>>(s: S, must_encode: impl Fn(u8) -> bool) -> String {
out
}
fn convert_port<N>(port_str: N) -> Result<u16, String>
fn convert_port<N>(port_str: N) -> Result<u16, SplitUrlError>
where
N: AsRef<str>,
{
port_str
.as_ref()
.parse::<u16>()
.map_err(|e| format!("Invalid port: {}", e))
.map_err(|e| SplitUrlError::new(format!("Invalid port: {}", e)))
}
///////////////////////////////////////////////////////////////////////////////
#[derive(ThisError, Debug, Clone, Eq, PartialEq)]
#[error("SplitUrlError: {0}")]
pub struct SplitUrlError(String);
impl SplitUrlError {
pub fn new<T: ToString>(message: T) -> Self {
SplitUrlError(message.to_string())
}
}
///////////////////////////////////////////////////////////////////////////////
@@ -163,7 +177,7 @@ impl SplitUrlPath {
}
impl FromStr for SplitUrlPath {
type Err = String;
type Err = SplitUrlError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(if let Some((p, q)) = s.split_once('?') {
if let Some((p, f)) = p.split_once('#') {
@@ -213,17 +227,17 @@ pub enum SplitUrlHost {
}
impl SplitUrlHost {
pub fn new<S: AsRef<str>>(s: S) -> Result<Self, String> {
pub fn new<S: AsRef<str>>(s: S) -> Result<Self, SplitUrlError> {
Self::from_str(s.as_ref())
}
}
impl FromStr for SplitUrlHost {
type Err = String;
type Err = SplitUrlError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Err("Host is empty".to_owned());
return Err(SplitUrlError::new("Host is empty"));
}
if let Ok(v4) = Ipv4Addr::from_str(s) {
return Ok(SplitUrlHost::IpAddr(IpAddr::V4(v4)));
@@ -232,13 +246,13 @@ impl FromStr for SplitUrlHost {
if let Ok(v6) = Ipv6Addr::from_str(&s[1..s.len() - 1]) {
return Ok(SplitUrlHost::IpAddr(IpAddr::V6(v6)));
}
return Err("Invalid ipv6 address".to_owned());
return Err(SplitUrlError::new("Invalid ipv6 address"));
}
for ch in s.chars() {
if !matches!(ch,
'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '.' )
{
return Err("Invalid hostname".to_owned());
return Err(SplitUrlError::new("Invalid hostname"));
}
}
Ok(SplitUrlHost::Hostname(s.to_owned()))
@@ -311,11 +325,11 @@ fn split_host_with_port(s: &str) -> Option<(&str, &str)> {
}
impl FromStr for SplitUrl {
type Err = String;
type Err = SplitUrlError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some((scheme, mut rest)) = s.split_once("://") {
if !is_valid_scheme(scheme) {
return Err("Invalid scheme specified".to_owned());
return Err(SplitUrlError::new("Invalid scheme specified"));
}
let userinfo = {
if let Some((userinfo_str, after)) = rest.split_once('@') {
@@ -350,7 +364,7 @@ impl FromStr for SplitUrl {
Ok(SplitUrl::new(scheme, userinfo, host, None, None))
}
} else {
Err("No scheme specified".to_owned())
Err(SplitUrlError::new("No scheme specified"))
}
}
}
+22 -26
View File
@@ -5,26 +5,33 @@ use once_cell::sync::OnceCell;
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
type TickTaskRoutine =
dyn Fn(StopToken, u64, u64) -> PinBoxFuture<Result<(), String>> + 'static;
type TickTaskRoutine<E> =
dyn Fn(StopToken, u64, u64) -> PinBoxFuture<Result<(), E>> + 'static;
} else {
type TickTaskRoutine =
dyn Fn(StopToken, u64, u64) -> SendPinBoxFuture<Result<(), String>> + Send + Sync + 'static;
type TickTaskRoutine<E> =
dyn Fn(StopToken, u64, u64) -> SendPinBoxFuture<Result<(), E>> + Send + Sync + 'static;
}
}
/// Runs a single-future background processing task, attempting to run it once every 'tick period' microseconds.
/// If the prior tick is still running, it will allow it to finish, and do another tick when the timer comes around again.
/// One should attempt to make tasks short-lived things that run in less than the tick period if you want things to happen with regular periodicity.
pub struct TickTask {
pub struct TickTask<
#[cfg(target_arch = "wasm32")] E: 'static,
#[cfg(not(target_arch = "wasm32"))] E: Send + 'static,
> {
last_timestamp_us: AtomicU64,
tick_period_us: u64,
routine: OnceCell<Box<TickTaskRoutine>>,
routine: OnceCell<Box<TickTaskRoutine<E>>>,
stop_source: AsyncMutex<Option<StopSource>>,
single_future: MustJoinSingleFuture<Result<(), String>>,
single_future: MustJoinSingleFuture<Result<(), E>>,
}
impl TickTask {
impl<
#[cfg(target_arch = "wasm32")] E: 'static,
#[cfg(not(target_arch = "wasm32"))] E: Send + 'static,
> TickTask<E>
{
pub fn new_us(tick_period_us: u64) -> Self {
Self {
last_timestamp_us: AtomicU64::new(0),
@@ -53,25 +60,14 @@ impl TickTask {
}
}
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
pub fn set_routine(
&self,
routine: impl Fn(StopToken, u64, u64) -> PinBoxFuture<Result<(), String>> + 'static,
) {
self.routine.set(Box::new(routine)).map_err(drop).unwrap();
}
} else {
pub fn set_routine(
&self,
routine: impl Fn(StopToken, u64, u64) -> SendPinBoxFuture<Result<(), String>> + Send + Sync + 'static,
) {
self.routine.set(Box::new(routine)).map_err(drop).unwrap();
}
}
pub fn set_routine(
&self,
routine: impl Fn(StopToken, u64, u64) -> SendPinBoxFuture<Result<(), E>> + Send + Sync + 'static,
) {
self.routine.set(Box::new(routine)).map_err(drop).unwrap();
}
pub async fn stop(&self) -> Result<(), String> {
pub async fn stop(&self) -> Result<(), E> {
// drop the stop source if we have one
let opt_stop_source = &mut *self.stop_source.lock().await;
if opt_stop_source.is_none() {
@@ -89,7 +85,7 @@ impl TickTask {
}
}
pub async fn tick(&self) -> Result<(), String> {
pub async fn tick(&self) -> Result<(), E> {
let now = intf::get_timestamp();
let last_timestamp_us = self.last_timestamp_us.load(Ordering::Acquire);
+98
View File
@@ -0,0 +1,98 @@
use super::*;
use cfg_if::*;
use core::fmt::{Debug, Display};
use core::result::Result;
use std::error::Error;
use std::io;
#[derive(ThisError, Debug, Clone, Copy, Eq, PartialEq)]
#[error("Timeout")]
pub struct TimeoutError();
impl TimeoutError {
pub fn to_io(self) -> io::Error {
io::Error::new(io::ErrorKind::TimedOut, self)
}
}
cfg_if! {
if #[cfg(feature="rt-async-std")] {
impl From<async_std::future::TimeoutError> for TimeoutError {
fn from(_: async_std::future::TimeoutError) -> Self {
Self()
}
}
} else if #[cfg(feature="rt-tokio")] {
impl From<tokio::time::error::Elapsed> for TimeoutError {
fn from(_: tokio::time::error::Elapsed) -> Self {
Self()
}
}
}
}
//////////////////////////////////////////////////////////////////
pub enum TimeoutOr<T> {
Timeout,
Value(T),
}
impl<T> TimeoutOr<T> {
pub fn timeout() -> Self {
Self::Timeout
}
pub fn value(value: T) -> Self {
Self::Value(value)
}
pub fn is_timeout(&self) -> bool {
matches!(self, Self::Timeout)
}
pub fn is_value(&self) -> bool {
matches!(self, Self::Value(_))
}
pub fn ok(self) -> Result<T, TimeoutError> {
match self {
Self::Timeout => Err(TimeoutError {}),
Self::Value(v) => Ok(v),
}
}
}
impl<T> From<TimeoutOr<T>> for Option<T> {
fn from(t: TimeoutOr<T>) -> Self {
match t {
TimeoutOr::<T>::Timeout => None,
TimeoutOr::<T>::Value(v) => Some(v),
}
}
}
impl<T: Clone> Clone for TimeoutOr<T> {
fn clone(&self) -> Self {
match self {
Self::Timeout => Self::Timeout,
Self::Value(t) => Self::Value(t.clone()),
}
}
}
impl<T: Debug> Debug for TimeoutOr<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Timeout => write!(f, "Timeout"),
Self::Value(arg0) => f.debug_tuple("Value").field(arg0).finish(),
}
}
}
impl<T: Display> Display for TimeoutOr<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Timeout => write!(f, ""),
Self::Value(arg0) => write!(f, "{}", arg0),
}
}
}
impl<T: Debug + Display> Error for TimeoutOr<T> {}
+30 -25
View File
@@ -1,5 +1,6 @@
use crate::xx::*;
use alloc::string::ToString;
use std::io;
use std::path::Path;
#[macro_export]
@@ -11,6 +12,24 @@ macro_rules! assert_err {
};
}
#[macro_export]
macro_rules! io_error_other {
($msg:expr) => {
io::Error::new(io::ErrorKind::Other, $msg.to_string())
};
}
pub fn to_io_error_other<E: std::error::Error + Send + Sync + 'static>(x: E) -> io::Error {
io::Error::new(io::ErrorKind::Other, x)
}
#[macro_export]
macro_rules! bail_io_error_other {
($msg:expr) => {
return io::Result::Err(io::Error::new(io::ErrorKind::Other, $msg.to_string()))
};
}
pub fn split_port(name: &str) -> Result<(String, Option<u16>), String> {
if let Some(split) = name.rfind(':') {
let hoststr = &name[0..split];
@@ -129,7 +148,7 @@ where
}
}
pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result<Vec<SocketAddr>, String> {
pub fn listen_address_to_socket_addrs(listen_address: &str) -> EyreResult<Vec<SocketAddr>> {
// If no address is specified, but the port is, use ipv4 and ipv6 unspecified
// If the address is specified, only use the specified port and fail otherwise
let ip_addrs = vec![
@@ -138,12 +157,9 @@ pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result<Vec<Socket
];
Ok(if let Some(portstr) = listen_address.strip_prefix(':') {
let port = portstr.parse::<u16>().map_err(|_| {
format!(
"Invalid port format in udp listen address: {}",
listen_address
)
})?;
let port = portstr
.parse::<u16>()
.wrap_err("Invalid port format in udp listen address")?;
ip_addrs.iter().map(|a| SocketAddr::new(*a, port)).collect()
} else if let Ok(port) = listen_address.parse::<u16>() {
ip_addrs.iter().map(|a| SocketAddr::new(*a, port)).collect()
@@ -151,11 +167,11 @@ pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result<Vec<Socket
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
use core::str::FromStr;
vec![SocketAddr::from_str(listen_address).map_err(|_| format!("Unable to parse address: {}", listen_address))?]
vec![SocketAddr::from_str(listen_address).wrap_err("Unable to parse address")?]
} else {
listen_address
.to_socket_addrs()
.map_err(|_| format!("Unable to resolve address: {}", listen_address))?
.wrap_err("Unable to resolve address")?
.collect()
}
}
@@ -185,7 +201,7 @@ cfg_if::cfg_if! {
use std::os::unix::prelude::PermissionsExt;
use nix::unistd::{Uid, Gid};
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(), String>
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> EyreResult<()>
{
let path = path.as_ref();
if !path.exists() {
@@ -194,13 +210,13 @@ cfg_if::cfg_if! {
let uid = Uid::effective();
let gid = Gid::effective();
let meta = std::fs::metadata(path).map_err(|e| format!("unable to get metadata for path '{:?}': {}",path, e))?;
let meta = std::fs::metadata(path).wrap_err("unable to get metadata for path")?;
if meta.mode() != 0o600 {
std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).map_err(|e| format!("unable to set correct permissions on path '{:?}': {}", path, e))?;
std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).wrap_err("unable to set correct permissions on path")?;
}
if meta.uid() != uid.as_raw() || meta.gid() != gid.as_raw() {
return Err(format!("path has incorrect owner/group: {:?}", path));
bail!("path has incorrect owner/group");
}
Ok(())
}
@@ -208,24 +224,13 @@ cfg_if::cfg_if! {
//use std::os::windows::fs::MetadataExt;
//use windows_permissions::*;
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(),String>
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> EyreResult<()>
{
let path = path.as_ref();
if !path.exists() {
return Ok(());
}
// let uid = Uid::effective();
// let gid = Gid::effective();
//let meta = std::fs::metadata(path).map_err(|e| format!("unable to get metadata for path '{:?}': {}",path, e))?;
//if meta.mode() != 0o600 {
// std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).map_err(|e| format!("unable to set correct permissions on path '{:?}': {}", path, e))?;
//}
//if meta.uid() != uid.as_raw() || meta.gid() != gid.as_raw() {
// chown(path, Some(uid), Some(gid)).map_err(|e| format!("unable to set correct owner on path '{:?}': {}", path, e))?;
//}
Ok(())
}
} else {