use std::{
error::Error as StdError,
fmt::{Debug, Display},
io::{Cursor, Read, Result, Write},
num::{ParseFloatError, ParseIntError},
sync::atomic::{AtomicU64, Ordering},
};
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use chrono::Duration;
use tracing::error;
use crate::{constants, status_code::StatusCode, Context, QualifiedName};
#[derive(Debug, Clone, Default)]
pub enum DataEncoding {
#[default]
Binary,
XML,
JSON,
Other(QualifiedName),
}
#[derive(Debug, Clone, Default)]
pub enum BuiltInDataEncoding {
#[default]
Binary,
XML,
JSON,
}
impl DataEncoding {
pub fn from_browse_name(name: QualifiedName) -> std::result::Result<Self, StatusCode> {
match name.name.as_ref() {
"Default Binary" | "" => Ok(Self::Binary),
"Default XML" => Ok(Self::XML),
"Default JSON" => Ok(Self::JSON),
_ if name.namespace_index != 0 => Ok(Self::Other(name)),
_ => Err(StatusCode::BadDataEncodingInvalid),
}
}
}
pub type EncodingResult<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error {
status: StatusCode,
request_id: Option<u32>,
request_handle: Option<u32>,
context: Box<dyn StdError + Send + Sync>,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", self.status(), self.context)
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
Some(&*self.context)
}
}
impl Error {
pub fn new(status: StatusCode, context: impl Into<Box<dyn StdError + Send + Sync>>) -> Self {
Self {
status,
request_handle: None,
request_id: None,
context: context.into(),
}
}
pub fn decoding(context: impl Into<Box<dyn StdError + Send + Sync>>) -> Self {
Self {
status: StatusCode::BadDecodingError,
request_handle: None,
request_id: None,
context: context.into(),
}
}
pub fn encoding(context: impl Into<Box<dyn StdError + Send + Sync>>) -> Self {
Self {
status: StatusCode::BadEncodingError,
request_handle: None,
request_id: None,
context: context.into(),
}
}
pub fn with_context(mut self, request_id: Option<u32>, request_handle: Option<u32>) -> Self {
self.request_id = request_id;
self.request_handle = request_handle;
self
}
pub fn with_request_id(mut self, id: u32) -> Self {
self.request_id = Some(id);
self
}
pub fn with_request_handle(mut self, handle: u32) -> Self {
self.request_handle = Some(handle);
self
}
pub fn maybe_with_request_handle(mut self, handle: Option<u32>) -> Self {
if let Some(handle) = handle {
self.request_handle = Some(handle);
}
self
}
pub fn status(&self) -> StatusCode {
self.status
}
pub fn full_context(&self) -> Option<(u32, u32)> {
if let (Some(id), Some(handle)) = (self.request_id, self.request_handle) {
Some((id, handle))
} else {
None
}
}
}
impl From<Error> for StatusCode {
fn from(value: Error) -> Self {
error!("{}", value);
value.status()
}
}
impl From<Error> for std::io::Error {
fn from(value: Error) -> Self {
value.status().into()
}
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Self::decoding(value)
}
}
impl From<ParseIntError> for Error {
fn from(value: ParseIntError) -> Self {
Self::decoding(value)
}
}
impl From<ParseFloatError> for Error {
fn from(value: ParseFloatError) -> Self {
Self::decoding(value)
}
}
#[derive(Debug)]
pub struct DepthLock<'a> {
depth_gauge: &'a DepthGauge,
}
impl Drop for DepthLock<'_> {
fn drop(&mut self) {
self.depth_gauge
.current_depth
.fetch_sub(1, Ordering::Release);
}
}
impl<'a> DepthLock<'a> {
fn new(depth_gauge: &'a DepthGauge) -> (Self, u64) {
let current = depth_gauge.current_depth.fetch_add(1, Ordering::Acquire);
(Self { depth_gauge }, current)
}
pub fn obtain(depth_gauge: &'a DepthGauge) -> core::result::Result<DepthLock<'a>, Error> {
let max_depth = depth_gauge.max_depth;
let (gauge, val) = Self::new(depth_gauge);
if val >= max_depth {
Err(Error::decoding(
"Decoding in stream aborted due maximum recursion depth being reached",
))
} else {
Ok(gauge)
}
}
}
#[derive(Debug)]
pub struct DepthGauge {
pub(self) max_depth: u64,
pub(self) current_depth: AtomicU64,
}
impl Clone for DepthGauge {
fn clone(&self) -> Self {
Self {
max_depth: self.max_depth,
current_depth: AtomicU64::new(0),
}
}
}
impl Default for DepthGauge {
fn default() -> Self {
Self::new(constants::MAX_DECODING_DEPTH)
}
}
impl DepthGauge {
pub fn new(max_depth: u64) -> Self {
Self {
max_depth,
current_depth: AtomicU64::new(0),
}
}
pub fn minimal() -> Self {
Self {
max_depth: 1,
..Default::default()
}
}
pub fn max_depth(&self) -> u64 {
self.max_depth
}
}
#[derive(Clone, Debug)]
pub struct DecodingOptions {
pub client_offset: Duration,
pub max_message_size: usize,
pub max_chunk_count: usize,
pub max_string_length: usize,
pub max_byte_string_length: usize,
pub max_array_length: usize,
pub decoding_depth_gauge: DepthGauge,
}
impl Default for DecodingOptions {
fn default() -> Self {
DecodingOptions {
client_offset: Duration::zero(),
max_message_size: constants::MAX_MESSAGE_SIZE,
max_chunk_count: constants::MAX_CHUNK_COUNT,
max_string_length: constants::MAX_STRING_LENGTH,
max_byte_string_length: constants::MAX_BYTE_STRING_LENGTH,
max_array_length: constants::MAX_ARRAY_LENGTH,
decoding_depth_gauge: DepthGauge::default(),
}
}
}
impl DecodingOptions {
pub fn minimal() -> Self {
DecodingOptions {
max_string_length: 8192,
max_byte_string_length: 8192,
max_array_length: 8192,
decoding_depth_gauge: DepthGauge::minimal(),
..Default::default()
}
}
pub fn test() -> Self {
Self::default()
}
pub fn depth_lock(&self) -> core::result::Result<DepthLock<'_>, Error> {
DepthLock::obtain(&self.decoding_depth_gauge)
}
}
pub trait UaNullable {
fn is_ua_null(&self) -> bool {
false
}
}
impl<T> UaNullable for Option<T>
where
T: UaNullable,
{
fn is_ua_null(&self) -> bool {
match self {
Some(s) => s.is_ua_null(),
None => true,
}
}
}
impl<T> UaNullable for Vec<T> where T: UaNullable {}
impl<T> UaNullable for Box<T>
where
T: UaNullable,
{
fn is_ua_null(&self) -> bool {
self.as_ref().is_ua_null()
}
}
macro_rules! is_null_const {
($t:ty, $c:expr) => {
impl UaNullable for $t {
fn is_ua_null(&self) -> bool {
*self == $c
}
}
};
}
is_null_const!(bool, false);
is_null_const!(u8, 0);
is_null_const!(u16, 0);
is_null_const!(u32, 0);
is_null_const!(u64, 0);
is_null_const!(i8, 0);
is_null_const!(i16, 0);
is_null_const!(i32, 0);
is_null_const!(i64, 0);
is_null_const!(f32, 0.0);
is_null_const!(f64, 0.0);
impl UaNullable for String {}
impl UaNullable for str {}
pub trait BinaryEncodable {
#[allow(unused)]
fn byte_len(&self, ctx: &crate::Context<'_>) -> usize;
fn encode<S: Write + ?Sized>(&self, stream: &mut S, ctx: &Context<'_>) -> EncodingResult<()>;
fn override_encoding(&self) -> Option<BuiltInDataEncoding> {
None
}
fn encode_to_vec(&self, ctx: &Context<'_>) -> Vec<u8> {
let mut buffer = Cursor::new(Vec::with_capacity(self.byte_len(ctx)));
let _ = self.encode(&mut buffer, ctx);
buffer.into_inner()
}
}
pub trait BinaryDecodable: Sized {
fn decode<S: Read + ?Sized>(stream: &mut S, ctx: &Context<'_>) -> EncodingResult<Self>;
}
pub trait SimpleBinaryEncodable {
#[allow(unused)]
fn byte_len(&self) -> usize;
fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()>;
fn encode_to_vec(&self) -> Vec<u8> {
let mut buffer = Cursor::new(Vec::with_capacity(self.byte_len()));
let _ = self.encode(&mut buffer);
buffer.into_inner()
}
}
impl<T> BinaryEncodable for T
where
T: SimpleBinaryEncodable,
{
fn byte_len(&self, _ctx: &crate::Context<'_>) -> usize {
SimpleBinaryEncodable::byte_len(self)
}
fn encode<S: Write + ?Sized>(&self, stream: &mut S, _ctx: &Context<'_>) -> EncodingResult<()> {
SimpleBinaryEncodable::encode(self, stream)
}
}
pub trait SimpleBinaryDecodable: Sized {
fn decode<S: Read + ?Sized>(
stream: &mut S,
decoding_options: &DecodingOptions,
) -> EncodingResult<Self>;
}
impl<T> BinaryDecodable for T
where
T: SimpleBinaryDecodable,
{
fn decode<S: Read + ?Sized>(stream: &mut S, ctx: &Context<'_>) -> EncodingResult<Self> {
SimpleBinaryDecodable::decode(stream, ctx.options())
}
}
pub fn process_encode_io_result(result: Result<()>) -> EncodingResult<()> {
result.map_err(Error::encoding)
}
pub fn process_decode_io_result<T>(result: Result<T>) -> EncodingResult<T>
where
T: Debug,
{
result.map_err(Error::decoding)
}
impl<T> BinaryEncodable for Option<Vec<T>>
where
T: BinaryEncodable,
{
fn byte_len(&self, ctx: &crate::Context<'_>) -> usize {
let mut size = 4;
if let Some(ref values) = self {
size += values.iter().map(|v| v.byte_len(ctx)).sum::<usize>();
}
size
}
fn encode<S: Write + ?Sized>(&self, stream: &mut S, ctx: &Context<'_>) -> EncodingResult<()> {
if let Some(ref values) = self {
write_i32(stream, values.len() as i32)?;
for value in values.iter() {
value.encode(stream, ctx)?;
}
} else {
write_i32(stream, -1)?;
}
Ok(())
}
}
impl<T> BinaryDecodable for Option<Vec<T>>
where
T: BinaryDecodable,
{
fn decode<S: Read + ?Sized>(
stream: &mut S,
ctx: &Context<'_>,
) -> EncodingResult<Option<Vec<T>>> {
let len = read_i32(stream)?;
if len == -1 {
Ok(None)
} else if len < -1 {
Err(Error::decoding(
"Array length is negative value and invalid",
))
} else if len as usize > ctx.options().max_array_length {
Err(Error::decoding(format!(
"Array length {} exceeds decoding limit {}",
len,
ctx.options().max_array_length
)))
} else {
let mut values: Vec<T> = Vec::with_capacity(len as usize);
for _ in 0..len {
values.push(T::decode(stream, ctx)?);
}
Ok(Some(values))
}
}
}
pub fn byte_len_array<T: BinaryEncodable>(values: &Option<Vec<T>>, ctx: &Context<'_>) -> usize {
let mut size = 4;
if let Some(ref values) = values {
size += values.iter().map(|v| v.byte_len(ctx)).sum::<usize>();
}
size
}
pub fn write_bytes<W: Write + ?Sized>(
stream: &mut W,
value: u8,
count: usize,
) -> EncodingResult<usize> {
for _ in 0..count {
stream.write_u8(value).map_err(Error::encoding)?;
}
Ok(count)
}
pub fn write_u8<T, W: Write + ?Sized>(stream: &mut W, value: T) -> EncodingResult<()>
where
T: Into<u8>,
{
let buf: [u8; 1] = [value.into()];
process_encode_io_result(stream.write_all(&buf))
}
pub fn write_i16<T, W: Write + ?Sized>(stream: &mut W, value: T) -> EncodingResult<()>
where
T: Into<i16>,
{
let mut buf = [0u8; 2];
LittleEndian::write_i16(&mut buf, value.into());
process_encode_io_result(stream.write_all(&buf))
}
pub fn write_u16<T, W: Write + ?Sized>(stream: &mut W, value: T) -> EncodingResult<()>
where
T: Into<u16>,
{
let mut buf = [0u8; 2];
LittleEndian::write_u16(&mut buf, value.into());
process_encode_io_result(stream.write_all(&buf))
}
pub fn write_i32<T, W: Write + ?Sized>(stream: &mut W, value: T) -> EncodingResult<()>
where
T: Into<i32>,
{
let mut buf = [0u8; 4];
LittleEndian::write_i32(&mut buf, value.into());
process_encode_io_result(stream.write_all(&buf))
}
pub fn write_u32<T, W: Write + ?Sized>(stream: &mut W, value: T) -> EncodingResult<()>
where
T: Into<u32>,
{
let mut buf = [0u8; 4];
LittleEndian::write_u32(&mut buf, value.into());
process_encode_io_result(stream.write_all(&buf))
}
pub fn write_i64<T, W: Write + ?Sized>(stream: &mut W, value: T) -> EncodingResult<()>
where
T: Into<i64>,
{
let mut buf = [0u8; 8];
LittleEndian::write_i64(&mut buf, value.into());
process_encode_io_result(stream.write_all(&buf))
}
pub fn write_u64<T, W: Write + ?Sized>(stream: &mut W, value: T) -> EncodingResult<()>
where
T: Into<u64>,
{
let mut buf = [0u8; 8];
LittleEndian::write_u64(&mut buf, value.into());
process_encode_io_result(stream.write_all(&buf))
}
pub fn write_f32<T, W: Write + ?Sized>(stream: &mut W, value: T) -> EncodingResult<()>
where
T: Into<f32>,
{
let mut buf = [0u8; 4];
LittleEndian::write_f32(&mut buf, value.into());
process_encode_io_result(stream.write_all(&buf))
}
pub fn write_f64<T, W: Write + ?Sized>(stream: &mut W, value: T) -> EncodingResult<()>
where
T: Into<f64>,
{
let mut buf = [0u8; 8];
LittleEndian::write_f64(&mut buf, value.into());
process_encode_io_result(stream.write_all(&buf))
}
pub fn read_bytes<R: Read + ?Sized>(stream: &mut R, buf: &mut [u8]) -> EncodingResult<usize> {
let result = stream.read_exact(buf);
process_decode_io_result(result)?;
Ok(buf.len())
}
pub fn read_u8<R: Read + ?Sized>(stream: &mut R) -> EncodingResult<u8> {
let mut buf = [0u8];
let result = stream.read_exact(&mut buf);
process_decode_io_result(result)?;
Ok(buf[0])
}
pub fn read_i16<R: Read + ?Sized>(stream: &mut R) -> EncodingResult<i16> {
let mut buf = [0u8; 2];
let result = stream.read_exact(&mut buf);
process_decode_io_result(result)?;
Ok(LittleEndian::read_i16(&buf))
}
pub fn read_u16<R: Read + ?Sized>(stream: &mut R) -> EncodingResult<u16> {
let mut buf = [0u8; 2];
let result = stream.read_exact(&mut buf);
process_decode_io_result(result)?;
Ok(LittleEndian::read_u16(&buf))
}
pub fn read_i32<R: Read + ?Sized>(stream: &mut R) -> EncodingResult<i32> {
let mut buf = [0u8; 4];
let result = stream.read_exact(&mut buf);
process_decode_io_result(result)?;
Ok(LittleEndian::read_i32(&buf))
}
pub fn read_u32<R: Read + ?Sized>(stream: &mut R) -> EncodingResult<u32> {
let mut buf = [0u8; 4];
let result = stream.read_exact(&mut buf);
process_decode_io_result(result)?;
Ok(LittleEndian::read_u32(&buf))
}
pub fn read_i64<R: Read + ?Sized>(stream: &mut R) -> EncodingResult<i64> {
let mut buf = [0u8; 8];
let result = stream.read_exact(&mut buf);
process_decode_io_result(result)?;
Ok(LittleEndian::read_i64(&buf))
}
pub fn read_u64<R: Read + ?Sized>(stream: &mut R) -> EncodingResult<u64> {
let mut buf = [0u8; 8];
let result = stream.read_exact(&mut buf);
process_decode_io_result(result)?;
Ok(LittleEndian::read_u64(&buf))
}
pub fn read_f32<R: Read + ?Sized>(stream: &mut R) -> EncodingResult<f32> {
let mut buf = [0u8; 4];
let result = stream.read_exact(&mut buf);
process_decode_io_result(result)?;
Ok(LittleEndian::read_f32(&buf))
}
pub fn read_f64<R: Read + ?Sized>(stream: &mut R) -> EncodingResult<f64> {
let mut buf = [0u8; 8];
let result = stream.read_exact(&mut buf);
process_decode_io_result(result)?;
Ok(LittleEndian::read_f64(&buf))
}
pub fn skip_bytes<R: Read + ?Sized>(stream: &mut R, bytes: u64) -> EncodingResult<()> {
std::io::copy(&mut stream.take(bytes), &mut std::io::sink())?;
Ok(())
}
#[macro_export]
macro_rules! impl_encoded_as {
($ty:ident, $from:expr, $to:expr, $byte_len:expr) => {
impl $crate::SimpleBinaryEncodable for $ty {
fn byte_len(&self) -> usize {
$byte_len(self)
}
fn encode<S: std::io::Write + ?Sized>(
&self,
stream: &mut S,
) -> $crate::EncodingResult<()> {
$to(self)?.encode(stream)
}
}
impl $crate::SimpleBinaryDecodable for $ty {
fn decode<S: std::io::Read + ?Sized>(
stream: &mut S,
decoding_options: &$crate::DecodingOptions,
) -> $crate::EncodingResult<Self> {
let inner = $crate::SimpleBinaryDecodable::decode(stream, decoding_options)?;
$from(inner)
}
}
#[cfg(feature = "json")]
impl $crate::json::JsonEncodable for $ty {
fn encode(
&self,
stream: &mut $crate::json::JsonStreamWriter<&mut dyn std::io::Write>,
ctx: &$crate::json::Context<'_>,
) -> $crate::EncodingResult<()> {
$to(self)?.encode(stream, ctx)
}
}
#[cfg(feature = "json")]
impl $crate::json::JsonDecodable for $ty {
fn decode(
stream: &mut $crate::json::JsonStreamReader<&mut dyn std::io::Read>,
ctx: &$crate::json::Context<'_>,
) -> $crate::EncodingResult<Self> {
let inner = $crate::json::JsonDecodable::decode(stream, ctx)?;
$from(inner)
}
}
#[cfg(feature = "xml")]
impl $crate::xml::XmlEncodable for $ty {
fn encode(
&self,
stream: &mut $crate::xml::XmlStreamWriter<&mut dyn std::io::Write>,
ctx: &$crate::xml::Context<'_>,
) -> $crate::EncodingResult<()> {
$to(self)?.encode(stream, ctx)
}
}
#[cfg(feature = "xml")]
impl $crate::xml::XmlDecodable for $ty {
fn decode(
stream: &mut $crate::xml::XmlStreamReader<&mut dyn std::io::Read>,
ctx: &$crate::xml::Context<'_>,
) -> $crate::EncodingResult<Self> {
let inner = $crate::xml::XmlDecodable::decode(stream, ctx)?;
$from(inner)
}
}
};
}
pub use impl_encoded_as;
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::{constants, DepthGauge, DepthLock};
use crate::StatusCode;
#[test]
fn depth_gauge() {
let dg = Arc::new(DepthGauge::default());
let max_depth = dg.max_depth();
assert_eq!(max_depth, constants::MAX_DECODING_DEPTH);
{
let mut v = Vec::new();
for _ in 0..max_depth {
v.push(DepthLock::obtain(&dg).unwrap());
}
{
assert_eq!(
dg.current_depth.load(std::sync::atomic::Ordering::Relaxed),
max_depth
);
}
assert_eq!(
DepthLock::obtain(&dg).unwrap_err().status,
StatusCode::BadDecodingError
);
}
{
assert_eq!(
dg.current_depth.load(std::sync::atomic::Ordering::Relaxed),
0
);
}
}
}