use core::convert::{TryFrom, TryInto};
use std::time::{Duration, SystemTime};
use self::property::{
Cluster, Connection, Destination, Listener, Plugin, Property, Request, Response, Route, Source,
Upstream,
};
use crate::error::format_err;
use crate::host::error::function;
use crate::host::{self, ByteString};
pub use self::types::{ResponseFlags, TrafficDirection};
mod property;
mod proxy_wasm;
mod types;
pub trait StreamInfo {
fn stream_property(&self, path: &[&str]) -> host::Result<Option<ByteString>>;
fn set_stream_property(&self, path: &[&str], value: &[u8]) -> host::Result<()>;
}
impl dyn StreamInfo {
pub fn default() -> &'static dyn StreamInfo {
&impls::Host
}
}
impl<'a> dyn StreamInfo + 'a {
pub fn request(&'a self) -> RequestInfo<'a> {
RequestInfo {
stream: StreamInfoAccessor { stream_info: self },
}
}
pub fn response(&'a self) -> ResponseInfo<'a> {
ResponseInfo {
stream: StreamInfoAccessor { stream_info: self },
}
}
pub fn connection(&'a self) -> ConnectionInfo<'a> {
ConnectionInfo {
stream: StreamInfoAccessor { stream_info: self },
}
}
pub fn upstream(&'a self) -> UpstreamInfo<'a> {
UpstreamInfo {
stream: StreamInfoAccessor { stream_info: self },
}
}
pub fn source(&'a self) -> SourceInfo<'a> {
SourceInfo {
stream: StreamInfoAccessor { stream_info: self },
}
}
pub fn destination(&'a self) -> DestinationInfo<'a> {
DestinationInfo {
stream: StreamInfoAccessor { stream_info: self },
}
}
pub fn listener(&'a self) -> ListenerInfo<'a> {
ListenerInfo {
stream: StreamInfoAccessor { stream_info: self },
}
}
pub fn route(&'a self) -> RouteInfo<'a> {
RouteInfo {
stream: StreamInfoAccessor { stream_info: self },
}
}
pub fn cluster(&'a self) -> ClusterInfo<'a> {
ClusterInfo {
stream: StreamInfoAccessor { stream_info: self },
}
}
pub fn plugin(&'a self) -> PluginInfo<'a> {
PluginInfo {
stream: StreamInfoAccessor { stream_info: self },
}
}
}
struct StreamInfoAccessor<'a> {
stream_info: &'a dyn StreamInfo,
}
impl<'a> StreamInfoAccessor<'a> {
fn property<T, W>(&self, prop: &Property<T, W>) -> host::Result<Option<T>>
where
T: TryFrom<proxy_wasm::Value<W>, Error = host::Error>,
{
if let Some(bytes) = self.stream_info.stream_property(prop.path())? {
let encoded = proxy_wasm::Value::<W>::new(bytes.into_bytes());
let decoded: host::Result<T> = encoded.try_into();
decoded.map(Option::from).map_err(|err| {
function("env", "proxy_get_property")
.into_parse_error(format_err!(
"value of property \"{:?}\" is not valid: {:?}",
prop.path(),
err
))
.into()
})
} else {
Ok(None)
}
}
}
pub struct RequestInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
impl<'a> RequestInfo<'a> {
pub fn header<K>(&self, name: K) -> host::Result<Option<ByteString>>
where
K: AsRef<str>,
{
self.stream.property(&Request::header(name.as_ref()))
}
pub fn id(&self) -> host::Result<Option<String>> {
self.stream.property(Request::ID)
}
pub fn time(&self) -> host::Result<Option<SystemTime>> {
self.stream.property(Request::TIME)
}
pub fn duration(&self) -> host::Result<Option<Duration>> {
self.stream.property(Request::DURATION)
}
pub fn size(&self) -> host::Result<Option<u64>> {
self.stream.property(Request::SIZE)
}
pub fn total_size(&self) -> host::Result<Option<u64>> {
self.stream.property(Request::TOTAL_SIZE)
}
pub fn protocol(&self) -> host::Result<Option<String>> {
self.stream.property(Request::PROTOCOL)
}
pub fn path(&self) -> host::Result<Option<String>> {
self.stream.property(Request::PATH)
}
pub fn url_path(&self) -> host::Result<Option<String>> {
self.stream.property(Request::URL_PATH)
}
pub fn host(&self) -> host::Result<Option<String>> {
self.stream.property(Request::HOST)
}
pub fn method(&self) -> host::Result<Option<String>> {
self.stream.property(Request::METHOD)
}
pub fn scheme(&self) -> host::Result<Option<String>> {
self.stream.property(Request::SCHEME)
}
pub fn referer(&self) -> host::Result<Option<ByteString>> {
self.stream.property(Request::REFERER)
}
pub fn user_agent(&self) -> host::Result<Option<ByteString>> {
self.stream.property(Request::USER_AGENT)
}
}
pub struct ResponseInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
impl<'a> ResponseInfo<'a> {
pub fn header<K>(&self, name: K) -> host::Result<Option<ByteString>>
where
K: AsRef<str>,
{
self.stream.property(&Response::header(name.as_ref()))
}
pub fn trailer<K>(&self, name: K) -> host::Result<Option<ByteString>>
where
K: AsRef<str>,
{
self.stream.property(&Response::trailer(name.as_ref()))
}
pub fn status_code(&self) -> host::Result<Option<u16>> {
self.stream.property(Response::STATUS_CODE)
}
pub fn size(&self) -> host::Result<Option<u64>> {
self.stream.property(Response::SIZE)
}
pub fn total_size(&self) -> host::Result<Option<u64>> {
self.stream.property(Response::TOTAL_SIZE)
}
pub fn flags(&self) -> host::Result<Option<ResponseFlags>> {
self.stream.property(Response::FLAGS)
}
pub fn grpc_status(&self) -> host::Result<Option<i32>> {
self.stream.property(Response::GRPC_STATUS)
}
}
pub struct ConnectionInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
pub struct DownstreamConnectionTlsInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
impl<'a> ConnectionInfo<'a> {
pub fn id(&self) -> host::Result<Option<u64>> {
self.stream.property(Connection::ID)
}
pub fn is_mtls(&self) -> host::Result<Option<bool>> {
self.stream.property(Connection::IS_MTLS)
}
pub fn requested_server_name(&self) -> host::Result<Option<String>> {
self.stream.property(Connection::REQUESTED_SERVER_NAME)
}
pub fn tls(&'a self) -> DownstreamConnectionTlsInfo<'a> {
DownstreamConnectionTlsInfo {
stream: StreamInfoAccessor {
stream_info: self.stream.stream_info,
},
}
}
}
impl<'a> DownstreamConnectionTlsInfo<'a> {
pub fn version(&self) -> host::Result<Option<String>> {
self.stream.property(Connection::TLS_VERSION)
}
pub fn subject_local_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Connection::SUBJECT_LOCAL_CERTIFICATE)
}
pub fn subject_peer_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Connection::SUBJECT_PEER_CERTIFICATE)
}
pub fn uri_san_local_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Connection::URI_SAN_LOCAL_CERTIFICATE)
}
pub fn uri_san_peer_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Connection::URI_SAN_PEER_CERTIFICATE)
}
pub fn dns_san_local_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Connection::DNS_SAN_LOCAL_CERTIFICATE)
}
pub fn dns_san_peer_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Connection::DNS_SAN_PEER_CERTIFICATE)
}
}
pub struct UpstreamInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
pub struct UpstreamConnectionTlsInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
impl<'a> UpstreamInfo<'a> {
pub fn address(&self) -> host::Result<Option<String>> {
self.stream.property(Upstream::ADDRESS)
}
pub fn port(&self) -> host::Result<Option<u32>> {
self.stream.property(Upstream::PORT)
}
pub fn local_address(&self) -> host::Result<Option<String>> {
self.stream.property(Upstream::LOCAL_ADDRESS)
}
pub fn transport_failure_reason(&self) -> host::Result<Option<String>> {
self.stream.property(Upstream::TRANSPORT_FAILURE_REASON)
}
pub fn tls(&'a self) -> UpstreamConnectionTlsInfo<'a> {
UpstreamConnectionTlsInfo {
stream: StreamInfoAccessor {
stream_info: self.stream.stream_info,
},
}
}
}
impl<'a> UpstreamConnectionTlsInfo<'a> {
pub fn version(&self) -> host::Result<Option<String>> {
self.stream.property(Upstream::TLS_VERSION)
}
pub fn subject_local_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Upstream::SUBJECT_LOCAL_CERTIFICATE)
}
pub fn subject_peer_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Upstream::SUBJECT_PEER_CERTIFICATE)
}
pub fn uri_san_local_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Upstream::URI_SAN_LOCAL_CERTIFICATE)
}
pub fn uri_san_peer_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Upstream::URI_SAN_PEER_CERTIFICATE)
}
pub fn dns_san_local_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Upstream::DNS_SAN_LOCAL_CERTIFICATE)
}
pub fn dns_san_peer_certificate(&self) -> host::Result<Option<String>> {
self.stream.property(Upstream::DNS_SAN_PEER_CERTIFICATE)
}
}
pub struct SourceInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
impl<'a> SourceInfo<'a> {
pub fn address(&self) -> host::Result<Option<String>> {
self.stream.property(Source::ADDRESS)
}
pub fn port(&self) -> host::Result<Option<u32>> {
self.stream.property(Source::PORT)
}
}
pub struct DestinationInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
impl<'a> DestinationInfo<'a> {
pub fn address(&self) -> host::Result<Option<String>> {
self.stream.property(Destination::ADDRESS)
}
pub fn port(&self) -> host::Result<Option<u32>> {
self.stream.property(Destination::PORT)
}
}
pub struct ListenerInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
impl<'a> ListenerInfo<'a> {
pub fn traffic_direction(&self) -> host::Result<Option<TrafficDirection>> {
self.stream.property(Listener::TRAFFIC_DIRECTION)
}
}
pub struct ClusterInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
impl<'a> ClusterInfo<'a> {
pub fn name(&self) -> host::Result<Option<String>> {
self.stream.property(Cluster::NAME)
}
}
pub struct RouteInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
impl<'a> RouteInfo<'a> {
pub fn name(&self) -> host::Result<Option<String>> {
self.stream.property(Route::NAME)
}
}
pub struct PluginInfo<'a> {
stream: StreamInfoAccessor<'a>,
}
impl<'a> PluginInfo<'a> {
pub fn name(&self) -> host::Result<Option<String>> {
self.stream.property(Plugin::NAME)
}
pub fn root_id(&self) -> host::Result<Option<String>> {
self.stream.property(Plugin::ROOT_ID)
}
pub fn vm_id(&self) -> host::Result<Option<String>> {
self.stream.property(Plugin::VM_ID)
}
}
mod impls {
use crate::abi::proxy_wasm::hostcalls;
use super::StreamInfo;
use crate::host::{self, ByteString};
pub(super) struct Host;
impl StreamInfo for Host {
fn stream_property(&self, path: &[&str]) -> host::Result<Option<ByteString>> {
hostcalls::get_property(path)
}
fn set_stream_property(&self, path: &[&str], value: &[u8]) -> host::Result<()> {
hostcalls::set_property(path, value)
}
}
}