From 48e04350d45f7f08a8fe03cf7cd33beacff94dc8 Mon Sep 17 00:00:00 2001 From: Yaroslav Skopets Date: Fri, 28 Aug 2020 19:57:31 +0200 Subject: [PATCH] represent header values using `Vec` instead of `String` (#15) --- Cargo.toml | 1 + src/bytestring.rs | 676 ++++++++++++++++++++++++++++++++++++++++++++++ src/hostcalls.rs | 438 +++++++++++++++++++++++------- src/lib.rs | 1 + src/traits.rs | 54 ++-- src/types.rs | 4 +- 6 files changed, 1049 insertions(+), 125 deletions(-) create mode 100644 src/bytestring.rs diff --git a/Cargo.toml b/Cargo.toml index 376b5ae1..4ba1baa5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ wee_alloc = "0.4" [dev-dependencies] version-sync = "0.9" chrono = "0.4" +bstr = "0.2" [profile.release] lto = true diff --git a/src/bytestring.rs b/src/bytestring.rs new file mode 100644 index 00000000..22d6fb70 --- /dev/null +++ b/src/bytestring.rs @@ -0,0 +1,676 @@ +// Copyright 2020 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::hash; +use core::ops; +use std::fmt; +use std::string::FromUtf8Error; + +/// Represents a borrowed string value that is not necessarily UTF-8 encoded, +/// e.g. an HTTP header value. +pub struct ByteStr { + bytes: [u8], +} + +impl ByteStr { + #[inline] + fn from_bytes(slice: &[u8]) -> &ByteStr { + unsafe { &*(slice as *const [u8] as *const ByteStr) } + } + + #[inline] + fn from_bytes_mut(slice: &mut [u8]) -> &mut ByteStr { + unsafe { &mut *(slice as *mut [u8] as *mut ByteStr) } + } + + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.bytes + } +} + +impl ops::Deref for ByteStr { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &self.bytes + } +} + +impl ops::DerefMut for ByteStr { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.bytes + } +} + +impl AsRef<[u8]> for ByteStr { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.bytes + } +} + +impl AsMut<[u8]> for ByteStr { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.bytes + } +} + +// Implementation borrowed from https://github.com/tokio-rs/bytes/blob/master/src/fmt/debug.rs +impl fmt::Debug for ByteStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "b\"")?; + for &b in &self.bytes { + // https://doc.rust-lang.org/reference/tokens.html#byte-escapes + if b == b'\n' { + write!(f, "\\n")?; + } else if b == b'\r' { + write!(f, "\\r")?; + } else if b == b'\t' { + write!(f, "\\t")?; + } else if b == b'\\' || b == b'"' { + write!(f, "\\{}", b as char)?; + } else if b == b'\0' { + write!(f, "\\0")?; + // ASCII printable + } else if b >= 0x20 && b < 0x7f { + write!(f, "{}", b as char)?; + } else { + write!(f, "\\x{:02x}", b)?; + } + } + write!(f, "\"")?; + Ok(()) + } +} + +impl fmt::Display for ByteStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&String::from_utf8_lossy(&self.bytes), f) + } +} + +impl ops::Index for ByteStr { + type Output = u8; + + #[inline] + fn index(&self, idx: usize) -> &u8 { + &self.as_bytes()[idx] + } +} + +impl ops::Index for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, _: ops::RangeFull) -> &ByteStr { + self + } +} + +impl ops::Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: ops::Range) -> &ByteStr { + ByteStr::from_bytes(&self.as_bytes()[r.start..r.end]) + } +} + +impl ops::Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: ops::RangeInclusive) -> &ByteStr { + ByteStr::from_bytes(&self.as_bytes()[*r.start()..=*r.end()]) + } +} + +impl ops::Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: ops::RangeFrom) -> &ByteStr { + ByteStr::from_bytes(&self.as_bytes()[r.start..]) + } +} + +impl ops::Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: ops::RangeTo) -> &ByteStr { + ByteStr::from_bytes(&self.as_bytes()[..r.end]) + } +} + +impl ops::Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: ops::RangeToInclusive) -> &ByteStr { + ByteStr::from_bytes(&self.as_bytes()[..=r.end]) + } +} + +impl ops::IndexMut for ByteStr { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut u8 { + &mut self.bytes[idx] + } +} + +impl ops::IndexMut for ByteStr { + #[inline] + fn index_mut(&mut self, _: ops::RangeFull) -> &mut ByteStr { + self + } +} + +impl ops::IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: ops::Range) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.bytes[r.start..r.end]) + } +} + +impl ops::IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: ops::RangeInclusive) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.bytes[*r.start()..=*r.end()]) + } +} + +impl ops::IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: ops::RangeFrom) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.bytes[r.start..]) + } +} + +impl ops::IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: ops::RangeTo) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.bytes[..r.end]) + } +} + +impl ops::IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: ops::RangeToInclusive) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.bytes[..=r.end]) + } +} + +impl Eq for ByteStr {} + +impl PartialEq for ByteStr { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq for ByteStr { + #[inline] + fn eq(&self, other: &String) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq> for ByteStr { + #[inline] + fn eq(&self, other: &Vec) -> bool { + self.as_bytes() == other.as_slice() + } +} + +impl PartialEq for ByteStr { + #[inline] + fn eq(&self, other: &str) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq<&str> for ByteStr { + #[inline] + fn eq(&self, other: &&str) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq<[u8]> for ByteStr { + #[inline] + fn eq(&self, other: &[u8]) -> bool { + self.as_bytes() == other + } +} + +impl PartialEq<&[u8]> for ByteStr { + #[inline] + fn eq(&self, other: &&[u8]) -> bool { + self.as_bytes() == *other + } +} + +impl PartialEq for String { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + *other == *self + } +} + +impl PartialEq for Vec { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + *other == *self + } +} + +impl PartialEq for str { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + *other == *self + } +} + +impl PartialEq for &str { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + *other == *self + } +} + +impl PartialEq for &[u8] { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + *other == *self + } +} + +impl PartialEq for [u8] { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + *other == *self + } +} + +impl hash::Hash for ByteStr { + fn hash(&self, state: &mut H) + where + H: hash::Hasher, + { + self.as_bytes().hash(state); + } +} + +/// Represents a string value that is not necessarily UTF-8 encoded, +/// e.g. an HTTP header value. +#[derive(Default, Eq, Clone)] +pub struct ByteString { + bytes: Vec, +} + +impl ByteString { + pub fn new() -> Self { + Self::default() + } + + pub fn is_empty(&self) -> bool { + self.as_ref().is_empty() + } + + pub fn len(&self) -> usize { + self.as_ref().len() + } + + pub fn as_bytes(&self) -> &[u8] { + self.as_ref() + } + + pub fn into_bytes(self) -> Vec { + self.bytes + } + + pub fn into_string(self) -> Result { + String::from_utf8(self.bytes) + } +} + +impl ops::Deref for ByteString { + type Target = ByteStr; + + #[inline] + fn deref(&self) -> &ByteStr { + ByteStr::from_bytes(&self.bytes) + } +} + +impl ops::DerefMut for ByteString { + #[inline] + fn deref_mut(&mut self) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.bytes) + } +} + +impl AsRef<[u8]> for ByteString { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.bytes + } +} + +impl AsMut<[u8]> for ByteString { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.bytes + } +} + +impl fmt::Display for ByteString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +impl fmt::Debug for ByteString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl From> for ByteString { + #[inline] + fn from(bytes: Vec) -> Self { + ByteString { bytes } + } +} + +impl From<&[u8]> for ByteString { + #[inline] + fn from(bytes: &[u8]) -> Self { + bytes.to_owned().into() + } +} + +impl From for ByteString { + #[inline] + fn from(text: String) -> Self { + text.into_bytes().into() + } +} + +impl From<&str> for ByteString { + #[inline] + fn from(text: &str) -> Self { + text.to_owned().into() + } +} + +impl From<&ByteString> for ByteString { + #[inline] + fn from(data: &ByteString) -> Self { + data.clone() + } +} + +impl PartialEq for ByteString { + #[inline] + fn eq(&self, other: &ByteString) -> bool { + self.bytes == other.bytes + } +} + +impl PartialEq for ByteString { + #[inline] + fn eq(&self, other: &String) -> bool { + self.as_ref() == other.as_bytes() + } +} + +impl PartialEq> for ByteString { + #[inline] + fn eq(&self, other: &Vec) -> bool { + self.as_ref() == other.as_slice() + } +} + +impl PartialEq for ByteString { + #[inline] + fn eq(&self, other: &str) -> bool { + self.as_ref() == other.as_bytes() + } +} + +impl PartialEq<&str> for ByteString { + #[inline] + fn eq(&self, other: &&str) -> bool { + self.as_ref() == other.as_bytes() + } +} + +impl PartialEq<[u8]> for ByteString { + #[inline] + fn eq(&self, other: &[u8]) -> bool { + self.as_ref() == other + } +} + +impl PartialEq<&[u8]> for ByteString { + #[inline] + fn eq(&self, other: &&[u8]) -> bool { + self.as_ref() == *other + } +} + +impl PartialEq for String { + #[inline] + fn eq(&self, other: &ByteString) -> bool { + *other == *self + } +} + +impl PartialEq for Vec { + #[inline] + fn eq(&self, other: &ByteString) -> bool { + *other == *self + } +} + +impl PartialEq for str { + #[inline] + fn eq(&self, other: &ByteString) -> bool { + *other == *self + } +} + +impl PartialEq for &str { + #[inline] + fn eq(&self, other: &ByteString) -> bool { + *other == *self + } +} + +impl PartialEq for [u8] { + #[inline] + fn eq(&self, other: &ByteString) -> bool { + *other == *self + } +} + +impl PartialEq for &[u8] { + #[inline] + fn eq(&self, other: &ByteString) -> bool { + *other == *self + } +} + +impl hash::Hash for ByteString { + fn hash(&self, state: &mut H) + where + H: hash::Hasher, + { + (&**self).hash(state); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + #[test] + fn test_bytestring_bstr_utf8() { + use bstr::ByteSlice; + + let string: ByteString = "hello".into(); + assert_eq!(string.is_utf8(), true); + assert_eq!(string.starts_with_str("hel"), true); + assert_eq!(string.ends_with_str("lo"), true); + } + + #[test] + fn test_bytestring_bstr_bytes() { + use bstr::ByteSlice; + + let bytes: ByteString = vec![144u8, 145u8, 146u8].into(); + assert_eq!(bytes.is_utf8(), false); + assert_eq!(bytes.starts_with_str(b"\x90"), true); + assert_eq!(bytes.ends_with_str(b"\x92"), true); + } + + #[test] + fn test_bytestring_display_utf8() { + let string: ByteString = "utf-8 encoded string".into(); + + assert_eq!(format!("{}", string), "utf-8 encoded string"); + } + + #[test] + fn test_bytestring_debug_utf8() { + let string: ByteString = "utf-8 encoded string".into(); + + assert_eq!(format!("{:?}", string), "b\"utf-8 encoded string\""); + } + + #[test] + fn test_bytestring_display_bytes() { + let bytes: ByteString = vec![144u8, 145u8, 146u8].into(); + + assert_eq!(format!("{}", bytes), "���"); + } + + #[test] + fn test_bytestring_debug_bytes() { + let bytes: ByteString = vec![144u8, 145u8, 146u8].into(); + + assert_eq!(format!("{:?}", bytes), "b\"\\x90\\x91\\x92\""); + } + + #[test] + fn test_bytestring_as_ref() { + fn receive(value: T) + where + T: AsRef<[u8]>, + { + value.as_ref(); + } + + let string: ByteString = "utf-8 encoded string".into(); + receive(string); + + let bytes: ByteString = vec![144u8, 145u8, 146u8].into(); + receive(bytes); + } + + #[test] + fn test_bytestring_substr_eq_utf8() { + let string: ByteString = "hello".into(); + + assert_eq!(string[1..=3], "ell"); + assert_eq!(string[1..=3], "ell".to_owned()); + + assert_eq!("ell", string[1..=3]); + assert_eq!("ell".to_owned(), string[1..=3]); + } + + #[test] + fn test_bytestring_substr_eq_bytes() { + let bytes: ByteString = vec![144u8, 145u8, 146u8].into(); + + assert_eq!(bytes[1..2], b"\x91" as &[u8]); + assert_eq!(bytes[1..2], b"\x91".to_vec()); + + assert_eq!(b"\x91" as &[u8], bytes[1..2]); + assert_eq!(b"\x91".to_vec(), bytes[1..2]); + } + + #[test] + fn test_bytestring_eq_string() { + let string: ByteString = "utf-8 encoded string".into(); + + assert_eq!(string, "utf-8 encoded string"); + assert_eq!(string, b"utf-8 encoded string" as &[u8]); + + assert_eq!("utf-8 encoded string", string); + assert_eq!(b"utf-8 encoded string" as &[u8], string); + + assert_eq!(string, string); + } + + #[test] + fn test_bytestring_eq_bytes() { + let bytes: ByteString = vec![144u8, 145u8, 146u8].into(); + + assert_eq!(bytes, vec![144u8, 145u8, 146u8]); + assert_eq!(bytes, b"\x90\x91\x92" as &[u8]); + + assert_eq!(vec![144u8, 145u8, 146u8], bytes); + assert_eq!(b"\x90\x91\x92" as &[u8], bytes); + + assert_eq!(bytes, bytes); + } + + fn hash(t: &T) -> u64 { + let mut h = DefaultHasher::new(); + t.hash(&mut h); + h.finish() + } + + #[test] + fn test_bytestring_hash_string() { + let string: ByteString = "utf-8 encoded string".into(); + + assert_ne!(hash(&string), hash(&"utf-8 encoded string")); + assert_eq!(hash(&string), hash(&b"utf-8 encoded string")); + + assert_ne!(hash(&"utf-8 encoded string"), hash(&string)); + assert_eq!(hash(&b"utf-8 encoded string"), hash(&string)); + } + + #[test] + fn test_bytestring_hash_bytes() { + let bytes: ByteString = vec![144u8, 145u8, 146u8].into(); + + assert_eq!(hash(&bytes), hash(&vec![144u8, 145u8, 146u8])); + assert_eq!(hash(&bytes), hash(&[144u8, 145u8, 146u8])); + + assert_eq!(hash(&vec![144u8, 145u8, 146u8]), hash(&bytes)); + assert_eq!(hash(&[144u8, 145u8, 146u8]), hash(&bytes)); + } +} diff --git a/src/hostcalls.rs b/src/hostcalls.rs index 75731e27..7394cb02 100644 --- a/src/hostcalls.rs +++ b/src/hostcalls.rs @@ -19,6 +19,15 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::error::{HostCallError, HostResponseError, Result}; +/// Represents empty headers map. +pub const NO_HEADERS: &[(&[u8], &[u8])] = &[]; + +/// Represents empty body. +pub const NO_BODY: Option<&[u8]> = None; + +/// Represents empty trailers map. +pub const NO_TRAILERS: &[(&[u8], &[u8])] = &[]; + mod abi { pub const PROXY_LOG: &str = "proxy_log"; pub const PROXY_GET_CURRENT_TIME_NANOSECONDS: &str = "proxy_get_current_time_nanoseconds"; @@ -52,6 +61,7 @@ extern "C" { fn proxy_log(level: LogLevel, message_data: *const u8, message_size: usize) -> Status; } +/// Logs a message at a given log level. pub fn log(level: LogLevel, message: &str) -> Result<()> { unsafe { match proxy_log(level, message.as_ptr(), message.len()) { @@ -65,6 +75,7 @@ extern "C" { fn proxy_get_current_time_nanoseconds(return_time: *mut u64) -> Status; } +/// Returns current system time. pub fn get_current_time() -> Result { let mut return_time: u64 = 0; unsafe { @@ -81,6 +92,7 @@ extern "C" { fn proxy_set_tick_period_milliseconds(period: u32) -> Status; } +/// Sets the timer to a given period. pub fn set_tick_period(period: Duration) -> Result<()> { unsafe { match proxy_set_tick_period_milliseconds(period.as_millis() as u32) { @@ -99,18 +111,17 @@ extern "C" { ) -> Status; } -pub fn get_configuration() -> Result> { +/// Returns configuration, e.g. VM configuration, extension configuration, etc. +pub fn get_configuration() -> Result> { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { match proxy_get_configuration(&mut return_data, &mut return_size) { Status::Ok => { if !return_data.is_null() { - Ok(Some(Vec::from_raw_parts( - return_data, - return_size, - return_size, - ))) + Ok(Vec::from_raw_parts(return_data, return_size, return_size)) + .map(ByteString::from) + .map(Option::from) } else { Ok(None) } @@ -130,7 +141,12 @@ extern "C" { ) -> Status; } -pub fn get_buffer(buffer_type: BufferType, start: usize, max_size: usize) -> Result> { +/// Returns content from a given buffer. +pub fn get_buffer( + buffer_type: BufferType, + start: usize, + max_size: usize, +) -> Result> { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { @@ -143,11 +159,9 @@ pub fn get_buffer(buffer_type: BufferType, start: usize, max_size: usize) -> Res ) { Status::Ok => { if !return_data.is_null() { - Ok(Some(Vec::from_raw_parts( - return_data, - return_size, - return_size, - ))) + Ok(Vec::from_raw_parts(return_data, return_size, return_size)) + .map(ByteString::from) + .map(Option::from) } else { Ok(None) } @@ -166,7 +180,8 @@ extern "C" { ) -> Status; } -pub fn get_map(map_type: MapType) -> Result> { +/// Returns all key-value pairs from a given map. +pub fn get_map(map_type: MapType) -> Result> { unsafe { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; @@ -174,7 +189,9 @@ pub fn get_map(map_type: MapType) -> Result> { Status::Ok => { if !return_data.is_null() { let serialized_map = Vec::from_raw_parts(return_data, return_size, return_size); - utils::deserialize_map(&serialized_map) + utils::deserialize_map(&serialized_map).map_err(|err| { + HostResponseError::new(abi::PROXY_GET_HEADER_MAP_PAIRS, err).into() + }) } else { Ok(Vec::new()) } @@ -192,7 +209,28 @@ extern "C" { ) -> Status; } -pub fn set_map(map_type: MapType, map: Vec<(&str, &str)>) -> Result<()> { +/// Sets all key-value pairs in a given map. +/// +/// # Examples +/// +/// ```no_run +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// use proxy_wasm::types::MapType; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// hostcalls::set_map(MapType::HttpRequestHeaders, &vec![ +/// (":method", "GET"), +/// (":path", "/stuff"), +/// ])?; +/// # Ok(()) +/// # } +/// ``` +pub fn set_map(map_type: MapType, map: &[(K, V)]) -> Result<()> +where + K: AsRef<[u8]>, + V: AsRef<[u8]>, +{ let serialized_map = utils::serialize_map(map); unsafe { match proxy_set_header_map_pairs(map_type, serialized_map.as_ptr(), serialized_map.len()) { @@ -212,25 +250,39 @@ extern "C" { ) -> Status; } -pub fn get_map_value(map_type: MapType, key: &str) -> Result> { +/// Returns value of a given key from a given map. +/// +/// # Examples +/// +/// ```no_run +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// use proxy_wasm::types::MapType; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// let value = hostcalls::get_map_value(MapType::HttpRequestHeaders, "authorization")?; +/// # Ok(()) +/// # } +/// ``` +pub fn get_map_value(map_type: MapType, key: K) -> Result> +where + K: AsRef<[u8]>, +{ let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { match proxy_get_header_map_value( map_type, - key.as_ptr(), - key.len(), + key.as_ref().as_ptr(), + key.as_ref().len(), &mut return_data, &mut return_size, ) { Status::Ok => { if !return_data.is_null() { - String::from_utf8(Vec::from_raw_parts(return_data, return_size, return_size)) + Ok(Vec::from_raw_parts(return_data, return_size, return_size)) + .map(ByteString::from) .map(Option::from) - .map_err(|err| { - HostResponseError::new(abi::PROXY_GET_HEADER_MAP_VALUE, err.into()) - .into() - }) } else { Ok(None) } @@ -258,15 +310,33 @@ extern "C" { ) -> Status; } -pub fn set_map_value(map_type: MapType, key: &str, value: Option<&str>) -> Result<()> { +/// Sets / replaces / removes value of given key from a given map. +/// +/// # Examples +/// +/// ```no_run +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// use proxy_wasm::types::MapType; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// hostcalls::set_map_value(MapType::HttpRequestHeaders, "authorization", Some("Bearer ..."))?; +/// # Ok(()) +/// # } +/// ``` +pub fn set_map_value(map_type: MapType, key: K, value: Option) -> Result<()> +where + K: AsRef<[u8]>, + V: AsRef<[u8]>, +{ unsafe { if let Some(value) = value { match proxy_replace_header_map_value( map_type, - key.as_ptr(), - key.len(), - value.as_ptr(), - value.len(), + key.as_ref().as_ptr(), + key.as_ref().len(), + value.as_ref().as_ptr(), + value.as_ref().len(), ) { Status::Ok => Ok(()), status => { @@ -274,7 +344,8 @@ pub fn set_map_value(map_type: MapType, key: &str, value: Option<&str>) -> Resul } } } else { - match proxy_remove_header_map_value(map_type, key.as_ptr(), key.len()) { + match proxy_remove_header_map_value(map_type, key.as_ref().as_ptr(), key.as_ref().len()) + { Status::Ok => Ok(()), status => { Err(HostCallError::new(abi::PROXY_REMOVE_HEADER_MAP_VALUE, status).into()) @@ -294,14 +365,32 @@ extern "C" { ) -> Status; } -pub fn add_map_value(map_type: MapType, key: &str, value: &str) -> Result<()> { +/// Adds a key-value pair to a given map. +/// +/// # Examples +/// +/// ```no_run +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// use proxy_wasm::types::MapType; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// hostcalls::add_map_value(MapType::HttpRequestHeaders, "authorization", "Bearer ...")?; +/// # Ok(()) +/// # } +/// ``` +pub fn add_map_value(map_type: MapType, key: K, value: V) -> Result<()> +where + K: AsRef<[u8]>, + V: AsRef<[u8]>, +{ unsafe { match proxy_add_header_map_value( map_type, - key.as_ptr(), - key.len(), - value.as_ptr(), - value.len(), + key.as_ref().as_ptr(), + key.as_ref().len(), + value.as_ref().as_ptr(), + value.as_ref().len(), ) { Status::Ok => Ok(()), status => Err(HostCallError::new(abi::PROXY_ADD_HEADER_MAP_VALUE, status).into()), @@ -318,7 +407,23 @@ extern "C" { ) -> Status; } -pub fn get_property(path: Vec<&str>) -> Result> { +/// Returns value of a property in the current context. +/// +/// # Examples +/// +/// ```no_run +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// let value = hostcalls::get_property(&["request", "time"])?; +/// # Ok(()) +/// # } +/// ``` +pub fn get_property

(path: &[P]) -> Result> +where + P: AsRef, +{ let serialized_path = utils::serialize_property_path(path); let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; @@ -331,11 +436,9 @@ pub fn get_property(path: Vec<&str>) -> Result> { ) { Status::Ok => { if !return_data.is_null() { - Ok(Some(Vec::from_raw_parts( - return_data, - return_size, - return_size, - ))) + Ok(Vec::from_raw_parts(return_data, return_size, return_size)) + .map(ByteString::from) + .map(Option::from) } else { Ok(None) } @@ -355,14 +458,34 @@ extern "C" { ) -> Status; } -pub fn set_property(path: Vec<&str>, value: Option<&[u8]>) -> Result<()> { +/// Sets property to a given value in the current context. +/// +/// # Examples +/// +/// ```no_run +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// hostcalls::set_property(&["my_filter", "my_property"], Some("my value"))?; +/// # Ok(()) +/// # } +/// ``` +pub fn set_property(path: &[P], value: Option) -> Result<()> +where + P: AsRef, + V: AsRef<[u8]>, +{ let serialized_path = utils::serialize_property_path(path); + let (value_ptr, value_len) = value.map_or((null(), 0), |value| { + (value.as_ref().as_ptr(), value.as_ref().len()) + }); unsafe { match proxy_set_property( serialized_path.as_ptr(), serialized_path.len(), - value.map_or(null(), |value| value.as_ptr()), - value.map_or(0, |value| value.len()), + value_ptr, + value_len, ) { Status::Ok => Ok(()), status => Err(HostCallError::new(abi::PROXY_SET_PROPERTY, status).into()), @@ -380,14 +503,30 @@ extern "C" { ) -> Status; } -pub fn get_shared_data(key: &str) -> Result<(Option, Option)> { +/// Returns shared data by key. +/// +/// # Examples +/// +/// ```no_run +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// let (data, version) = hostcalls::get_shared_data("my_shared_key")?; +/// # Ok(()) +/// # } +/// ``` +pub fn get_shared_data(key: K) -> Result<(Option, Option)> +where + K: AsRef, +{ let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; let mut return_cas: u32 = 0; unsafe { match proxy_get_shared_data( - key.as_ptr(), - key.len(), + key.as_ref().as_ptr(), + key.as_ref().len(), &mut return_data, &mut return_size, &mut return_cas, @@ -399,7 +538,8 @@ pub fn get_shared_data(key: &str) -> Result<(Option, Option)> { }; if !return_data.is_null() { Ok(( - Some(Vec::from_raw_parts(return_data, return_size, return_size)), + Some(Vec::from_raw_parts(return_data, return_size, return_size)) + .map(ByteString::from), cas, )) } else { @@ -422,13 +562,33 @@ extern "C" { ) -> Status; } -pub fn set_shared_data(key: &str, value: Option<&[u8]>, cas: Option) -> Result<()> { +/// Sets shared data by key. +/// +/// # Examples +/// +/// ```no_run +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// hostcalls::set_shared_data("my_shared_key", Some("my value"), None)?; +/// # Ok(()) +/// # } +/// ``` +pub fn set_shared_data(key: K, value: Option, cas: Option) -> Result<()> +where + K: AsRef, + V: AsRef<[u8]>, +{ + let (value_ptr, value_len) = value.map_or((null(), 0), |value| { + (value.as_ref().as_ptr(), value.as_ref().len()) + }); unsafe { match proxy_set_shared_data( - key.as_ptr(), - key.len(), - value.map_or(null(), |value| value.as_ptr()), - value.map_or(0, |value| value.len()), + key.as_ref().as_ptr(), + key.as_ref().len(), + value_ptr, + value_len, cas.unwrap_or(0), ) { Status::Ok => Ok(()), @@ -445,6 +605,7 @@ extern "C" { ) -> Status; } +/// Registers a shared queue with a given name. pub fn register_shared_queue(name: &str) -> Result { unsafe { let mut return_id: u32 = 0; @@ -465,6 +626,7 @@ extern "C" { ) -> Status; } +/// Looks up for an existing shared queue with a given name. pub fn resolve_shared_queue(vm_id: &str, name: &str) -> Result> { let mut return_id: u32 = 0; unsafe { @@ -490,18 +652,17 @@ extern "C" { ) -> Status; } -pub fn dequeue_shared_queue(queue_id: u32) -> Result> { +/// Returns data from the end of a given queue. +pub fn dequeue_shared_queue(queue_id: u32) -> Result> { let mut return_data: *mut u8 = null_mut(); let mut return_size: usize = 0; unsafe { match proxy_dequeue_shared_queue(queue_id, &mut return_data, &mut return_size) { Status::Ok => { if !return_data.is_null() { - Ok(Some(Vec::from_raw_parts( - return_data, - return_size, - return_size, - ))) + Ok(Vec::from_raw_parts(return_data, return_size, return_size)) + .map(ByteString::from) + .map(Option::from) } else { Ok(None) } @@ -520,13 +681,28 @@ extern "C" { ) -> Status; } -pub fn enqueue_shared_queue(queue_id: u32, value: Option<&[u8]>) -> Result<()> { +/// Adds a value to the front of a given queue. +/// +/// # Examples +/// +/// ```no_run +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// hostcalls::enqueue_shared_queue(1, Some("my value"))?; +/// # Ok(()) +/// # } +/// ``` +pub fn enqueue_shared_queue(queue_id: u32, value: Option) -> Result<()> +where + V: AsRef<[u8]>, +{ + let (value_ptr, value_len) = value.map_or((null(), 0), |value| { + (value.as_ref().as_ptr(), value.as_ref().len()) + }); unsafe { - match proxy_enqueue_shared_queue( - queue_id, - value.map_or(null(), |value| value.as_ptr()), - value.map_or(0, |value| value.len()), - ) { + match proxy_enqueue_shared_queue(queue_id, value_ptr, value_len) { Status::Ok => Ok(()), status => Err(HostCallError::new(abi::PROXY_ENQUEUE_SHARED_QUEUE, status).into()), } @@ -537,6 +713,7 @@ extern "C" { fn proxy_continue_request() -> Status; } +/// Resume processing of paused HTTP request. pub fn resume_http_request() -> Result<()> { unsafe { match proxy_continue_request() { @@ -550,6 +727,7 @@ extern "C" { fn proxy_continue_response() -> Status; } +/// Resume processing of paused HTTP response. pub fn resume_http_response() -> Result<()> { unsafe { match proxy_continue_response() { @@ -572,19 +750,44 @@ extern "C" { ) -> Status; } -pub fn send_http_response( +/// Sends HTTP response without forwarding request to the upstream. +/// +/// # Examples +/// +/// ```no_run +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// hostcalls::send_http_response( +/// 400, +/// hostcalls::NO_HEADERS, +/// hostcalls::NO_BODY, +/// )?; +/// # Ok(()) +/// # } +/// ``` +pub fn send_http_response( status_code: u32, - headers: Vec<(&str, &str)>, - body: Option<&[u8]>, -) -> Result<()> { + headers: &[(K, V)], + body: Option, +) -> Result<()> +where + K: AsRef<[u8]>, + V: AsRef<[u8]>, + B: AsRef<[u8]>, +{ let serialized_headers = utils::serialize_map(headers); + let (body_ptr, body_len) = body.map_or((null(), 0), |body| { + (body.as_ref().as_ptr(), body.as_ref().len()) + }); unsafe { match proxy_send_local_response( status_code, null(), 0, - body.map_or(null(), |body| body.as_ptr()), - body.map_or(0, |body| body.len()), + body_ptr, + body_len, serialized_headers.as_ptr(), serialized_headers.len(), -1, @@ -599,6 +802,7 @@ extern "C" { fn proxy_clear_route_cache() -> Status; } +/// Clears HTTP route cache. pub fn clear_http_route_cache() -> Result<()> { unsafe { match proxy_clear_route_cache() { @@ -623,15 +827,48 @@ extern "C" { ) -> Status; } -pub fn dispatch_http_call( +/// Dispatches an HTTP call to a given upstream. +/// +/// # Examples +/// +/// ```no_run +/// use std::time::Duration; +/// # use proxy_wasm_experimental as proxy_wasm; +/// use proxy_wasm::hostcalls; +/// +/// # fn action() -> proxy_wasm::error::Result<()> { +/// let request_handle = hostcalls::dispatch_http_call( +/// "cluster_name", +/// &vec![ +/// (":method", "POST"), +/// (":path", "/stuff"), +/// ], +/// Some("hi there!"), +/// hostcalls::NO_TRAILERS, +/// Duration::from_secs(10), +/// )?; +/// # Ok(()) +/// # } +/// ``` +pub fn dispatch_http_call( upstream: &str, - headers: Vec<(&str, &str)>, - body: Option<&[u8]>, - trailers: Vec<(&str, &str)>, + headers: &[(K1, V1)], + body: Option, + trailers: &[(K2, V2)], timeout: Duration, -) -> Result { +) -> Result +where + K1: AsRef<[u8]>, + V1: AsRef<[u8]>, + K2: AsRef<[u8]>, + V2: AsRef<[u8]>, + B: AsRef<[u8]>, +{ let serialized_headers = utils::serialize_map(headers); let serialized_trailers = utils::serialize_map(trailers); + let (body_ptr, body_len) = body.map_or((null(), 0), |body| { + (body.as_ref().as_ptr(), body.as_ref().len()) + }); let mut return_token: u32 = 0; unsafe { match proxy_http_call( @@ -639,8 +876,8 @@ pub fn dispatch_http_call( upstream.len(), serialized_headers.as_ptr(), serialized_headers.len(), - body.map_or(null(), |body| body.as_ptr()), - body.map_or(0, |body| body.len()), + body_ptr, + body_len, serialized_trailers.as_ptr(), serialized_trailers.len(), timeout.as_millis() as u32, @@ -659,6 +896,7 @@ extern "C" { fn proxy_set_effective_context(context_id: u32) -> Status; } +/// Changes the effective context. pub fn set_effective_context(context_id: u32) -> Result<()> { unsafe { match proxy_set_effective_context(context_id) { @@ -672,6 +910,7 @@ extern "C" { fn proxy_done() -> Status; } +/// Indicates to the host environment that Wasm VM side is done processing current context. pub fn done() -> Result<()> { unsafe { match proxy_done() { @@ -683,47 +922,54 @@ pub fn done() -> Result<()> { mod utils { use crate::error::Result; - use crate::types::Bytes; + use crate::types::ByteString; use std::convert::TryFrom; - pub(super) fn serialize_property_path(path: Vec<&str>) -> Bytes { + pub(super) fn serialize_property_path

(path: &[P]) -> Vec + where + P: AsRef, + { if path.is_empty() { return Vec::new(); } let mut size: usize = 0; - for part in &path { - size += part.len() + 1; + for part in path { + size += part.as_ref().len() + 1; } - let mut bytes: Bytes = Vec::with_capacity(size); - for part in &path { - bytes.extend_from_slice(&part.as_bytes()); + let mut bytes: Vec = Vec::with_capacity(size); + for part in path { + bytes.extend_from_slice(part.as_ref().as_bytes()); bytes.push(0); } bytes.pop(); bytes } - pub(super) fn serialize_map(map: Vec<(&str, &str)>) -> Bytes { + pub(super) fn serialize_map(map: &[(K, V)]) -> Vec + where + K: AsRef<[u8]>, + V: AsRef<[u8]>, + { let mut size: usize = 4; - for (name, value) in &map { - size += name.len() + value.len() + 10; + for (name, value) in map { + size += name.as_ref().len() + value.as_ref().len() + 10; } - let mut bytes: Bytes = Vec::with_capacity(size); + let mut bytes: Vec = Vec::with_capacity(size); bytes.extend_from_slice(&map.len().to_le_bytes()); - for (name, value) in &map { - bytes.extend_from_slice(&name.len().to_le_bytes()); - bytes.extend_from_slice(&value.len().to_le_bytes()); + for (name, value) in map { + bytes.extend_from_slice(&name.as_ref().len().to_le_bytes()); + bytes.extend_from_slice(&value.as_ref().len().to_le_bytes()); } - for (name, value) in &map { - bytes.extend_from_slice(&name.as_bytes()); + for (name, value) in map { + bytes.extend_from_slice(name.as_ref()); bytes.push(0); - bytes.extend_from_slice(&value.as_bytes()); + bytes.extend_from_slice(value.as_ref()); bytes.push(0); } bytes } - pub(super) fn deserialize_map(bytes: &[u8]) -> Result> { + pub(super) fn deserialize_map(bytes: &[u8]) -> Result> { let mut map = Vec::new(); if bytes.is_empty() { return Ok(map); @@ -738,7 +984,7 @@ mod utils { let size = u32::from_le_bytes(<[u8; 4]>::try_from(&bytes[s + 4..s + 8])?) as usize; let value = bytes[p..p + size].to_vec(); p += size + 1; - map.push((String::from_utf8(key)?, String::from_utf8(value)?)); + map.push((key.into(), value.into())); } Ok(map) } diff --git a/src/lib.rs b/src/lib.rs index e9a3fdaf..c4acf7f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ pub mod traits; pub mod types; mod allocator; +mod bytestring; mod dispatcher; mod logger; diff --git a/src/traits.rs b/src/traits.rs index 44f81e08..cc929d11 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -23,15 +23,15 @@ pub trait Context { hostcalls::get_current_time().unwrap() } - fn get_property(&self, path: Vec<&str>) -> Option { - hostcalls::get_property(path).unwrap() + fn get_property(&self, path: Vec<&str>) -> Option { + hostcalls::get_property(&path).unwrap() } fn set_property(&self, path: Vec<&str>, value: Option<&[u8]>) { - hostcalls::set_property(path, value).unwrap() + hostcalls::set_property(&path, value).unwrap() } - fn get_shared_data(&self, key: &str) -> (Option, Option) { + fn get_shared_data(&self, key: &str) -> (Option, Option) { hostcalls::get_shared_data(key).unwrap() } @@ -47,7 +47,7 @@ pub trait Context { hostcalls::resolve_shared_queue(vm_id, name).unwrap() } - fn dequeue_shared_queue(&self, queue_id: u32) -> Result> { + fn dequeue_shared_queue(&self, queue_id: u32) -> Result> { hostcalls::dequeue_shared_queue(queue_id) } @@ -63,7 +63,7 @@ pub trait Context { trailers: Vec<(&str, &str)>, timeout: Duration, ) -> Result { - hostcalls::dispatch_http_call(upstream, headers, body, trailers, timeout) + hostcalls::dispatch_http_call(upstream, &headers, body, &trailers, timeout) } fn on_http_call_response( @@ -75,15 +75,15 @@ pub trait Context { ) { } - fn get_http_call_response_headers(&self) -> Vec<(String, String)> { + fn get_http_call_response_headers(&self) -> Vec<(ByteString, ByteString)> { hostcalls::get_map(MapType::HttpCallResponseHeaders).unwrap() } - fn get_http_call_response_body(&self, start: usize, max_size: usize) -> Option { + fn get_http_call_response_body(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::HttpCallResponseBody, start, max_size).unwrap() } - fn get_http_call_response_trailers(&self) -> Vec<(String, String)> { + fn get_http_call_response_trailers(&self) -> Vec<(ByteString, ByteString)> { hostcalls::get_map(MapType::HttpCallResponseTrailers).unwrap() } @@ -111,7 +111,7 @@ pub trait RootContext: Context { true } - fn get_configuration(&self) -> Option { + fn get_configuration(&self) -> Option { hostcalls::get_configuration().unwrap() } @@ -141,7 +141,7 @@ pub trait StreamContext: Context { Action::Continue } - fn get_downstream_data(&self, start: usize, max_size: usize) -> Option { + fn get_downstream_data(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::DownstreamData, start, max_size).unwrap() } @@ -151,7 +151,7 @@ pub trait StreamContext: Context { Action::Continue } - fn get_upstream_data(&self, start: usize, max_size: usize) -> Option { + fn get_upstream_data(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::UpstreamData, start, max_size).unwrap() } @@ -165,15 +165,15 @@ pub trait HttpContext: Context { Action::Continue } - fn get_http_request_headers(&self) -> Vec<(String, String)> { + fn get_http_request_headers(&self) -> Vec<(ByteString, ByteString)> { hostcalls::get_map(MapType::HttpRequestHeaders).unwrap() } fn set_http_request_headers(&self, headers: Vec<(&str, &str)>) { - hostcalls::set_map(MapType::HttpRequestHeaders, headers).unwrap() + hostcalls::set_map(MapType::HttpRequestHeaders, &headers).unwrap() } - fn get_http_request_header(&self, name: &str) -> Option { + fn get_http_request_header(&self, name: &str) -> Option { hostcalls::get_map_value(MapType::HttpRequestHeaders, &name).unwrap() } @@ -189,7 +189,7 @@ pub trait HttpContext: Context { Action::Continue } - fn get_http_request_body(&self, start: usize, max_size: usize) -> Option { + fn get_http_request_body(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::HttpRequestBody, start, max_size).unwrap() } @@ -197,15 +197,15 @@ pub trait HttpContext: Context { Action::Continue } - fn get_http_request_trailers(&self) -> Vec<(String, String)> { + fn get_http_request_trailers(&self) -> Vec<(ByteString, ByteString)> { hostcalls::get_map(MapType::HttpRequestTrailers).unwrap() } fn set_http_request_trailers(&self, trailers: Vec<(&str, &str)>) { - hostcalls::set_map(MapType::HttpRequestTrailers, trailers).unwrap() + hostcalls::set_map(MapType::HttpRequestTrailers, &trailers).unwrap() } - fn get_http_request_trailer(&self, name: &str) -> Option { + fn get_http_request_trailer(&self, name: &str) -> Option { hostcalls::get_map_value(MapType::HttpRequestTrailers, &name).unwrap() } @@ -225,15 +225,15 @@ pub trait HttpContext: Context { Action::Continue } - fn get_http_response_headers(&self) -> Vec<(String, String)> { + fn get_http_response_headers(&self) -> Vec<(ByteString, ByteString)> { hostcalls::get_map(MapType::HttpResponseHeaders).unwrap() } fn set_http_response_headers(&self, headers: Vec<(&str, &str)>) { - hostcalls::set_map(MapType::HttpResponseHeaders, headers).unwrap() + hostcalls::set_map(MapType::HttpResponseHeaders, &headers).unwrap() } - fn get_http_response_header(&self, name: &str) -> Option { + fn get_http_response_header(&self, name: &str) -> Option { hostcalls::get_map_value(MapType::HttpResponseHeaders, &name).unwrap() } @@ -249,7 +249,7 @@ pub trait HttpContext: Context { Action::Continue } - fn get_http_response_body(&self, start: usize, max_size: usize) -> Option { + fn get_http_response_body(&self, start: usize, max_size: usize) -> Option { hostcalls::get_buffer(BufferType::HttpResponseBody, start, max_size).unwrap() } @@ -257,15 +257,15 @@ pub trait HttpContext: Context { Action::Continue } - fn get_http_response_trailers(&self) -> Vec<(String, String)> { + fn get_http_response_trailers(&self) -> Vec<(ByteString, ByteString)> { hostcalls::get_map(MapType::HttpResponseTrailers).unwrap() } fn set_http_response_trailers(&self, headers: Vec<(&str, &str)>) { - hostcalls::set_map(MapType::HttpResponseTrailers, headers).unwrap() + hostcalls::set_map(MapType::HttpResponseTrailers, &headers).unwrap() } - fn get_http_response_trailer(&self, name: &str) -> Option { + fn get_http_response_trailer(&self, name: &str) -> Option { hostcalls::get_map_value(MapType::HttpResponseTrailers, &name).unwrap() } @@ -287,7 +287,7 @@ pub trait HttpContext: Context { headers: Vec<(&str, &str)>, body: Option<&[u8]>, ) { - hostcalls::send_http_response(status_code, headers, body).unwrap() + hostcalls::send_http_response(status_code, &headers, body).unwrap() } fn clear_http_route_cache(&self) { diff --git a/src/types.rs b/src/types.rs index a287a2c1..95d02bf9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -14,6 +14,8 @@ use crate::traits::*; +pub use crate::bytestring::ByteString; + pub type NewRootContext = fn(context_id: u32) -> Box; pub type NewStreamContext = fn(context_id: u32, root_context_id: u32) -> Box; pub type NewHttpContext = fn(context_id: u32, root_context_id: u32) -> Box; @@ -75,5 +77,3 @@ pub enum PeerType { Local = 1, Remote = 2, } - -pub type Bytes = Vec;