proxy_sdk/property/
envoy.rs

1//! <https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes>
2//! Can be:
3//! * string for UTF-8 strings
4//! * bytes for byte buffers
5//! * int for 64-bit signed integers
6//! * uint for 64-bit unsigned integers
7//! * bool for booleans
8//! * list for lists of values
9//! * map for associative arrays with string keys
10//! * timestamp for timestamps as specified by Timestamp
11//! * duration for durations as specified by Duration
12//! * Protocol buffer message types
13
14use std::{
15    fmt,
16    net::SocketAddr,
17    time::{Duration, SystemTime},
18};
19
20use log::warn;
21
22use crate::property::all::AllAttributes;
23
24use super::{get_property_bool, get_property_decode, get_property_int, get_property_string};
25
26mod attributes_proto {
27    include!(concat!(env!("OUT_DIR"), "/proxywasm.attributes.rs"));
28}
29pub use attributes_proto::*;
30
31pub struct Attributes {
32    pub request: RequestAttributes,
33    pub response: ResponseAttributes,
34    pub connection: ConnectionAttributes,
35    pub upstream: UpstreamAttributes,
36    pub metadata: MetadataAttributes,
37    pub configuration: ConfigurationAttributes,
38    pub wasm: WasmAttributes,
39}
40
41impl Attributes {
42    // the internal attribute structs have a private field to ensure a user cant construct them
43    pub(crate) fn get() -> Self {
44        Self {
45            request: RequestAttributes(()),
46            response: ResponseAttributes(()),
47            connection: ConnectionAttributes(()),
48            upstream: UpstreamAttributes(()),
49            metadata: MetadataAttributes(()),
50            configuration: ConfigurationAttributes(()),
51            wasm: WasmAttributes(()),
52        }
53    }
54}
55
56impl fmt::Debug for Attributes {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        write!(f, "{:#?}", AllAttributes::get(self))
59    }
60}
61
62/// Available in HTTP filters during a request.
63pub struct RequestAttributes(());
64
65impl RequestAttributes {
66    /// The path portion of the URL
67    pub fn path(&self) -> Option<String> {
68        get_property_string("request.path")
69    }
70
71    /// The path portion of the URL without the query string
72    pub fn url_path(&self) -> Option<String> {
73        get_property_string("request.url_path")
74    }
75
76    /// The host portion of the URL
77    pub fn host(&self) -> Option<String> {
78        get_property_string("request.host")
79    }
80
81    /// The scheme portion of the URL e.g. “http”
82    pub fn scheme(&self) -> Option<String> {
83        get_property_string("request.scheme")
84    }
85
86    /// Request method e.g. “GET”
87    pub fn method(&self) -> Option<String> {
88        get_property_string("request.scheme")
89    }
90
91    /// All request headers indexed by the lower-cased header name
92    /// Header values in request.headers associative array are comma-concatenated in case of multiple values.
93    pub fn headers(&self) -> Option<Vec<(String, Vec<u8>)>> {
94        let headers = get_property_decode::<attributes_proto::StringMap>("request.headers")?;
95        Some(headers.map.into_iter().map(|x| (x.key, x.value)).collect())
96    }
97
98    /// Referer request header
99    pub fn referer(&self) -> Option<String> {
100        get_property_string("request.referer")
101    }
102
103    /// User agent request header
104    pub fn useragent(&self) -> Option<String> {
105        get_property_string("request.useragent")
106    }
107
108    /// Time of the first byte received
109    pub fn time(&self) -> Option<SystemTime> {
110        let raw = get_property_decode::<prost_types::Timestamp>("request.time")?;
111        if raw.seconds < 0 || raw.nanos < 0 {
112            warn!("request.time returned a negative timestamp, skipped");
113            None
114        } else {
115            Some(SystemTime::UNIX_EPOCH + Duration::new(raw.seconds as u64, raw.nanos as u32))
116        }
117    }
118
119    /// Request ID corresponding to x-request-id header value
120    pub fn id(&self) -> Option<String> {
121        get_property_string("request.id")
122    }
123
124    /// Request protocol (“HTTP/1.0”, “HTTP/1.1”, “HTTP/2”, or “HTTP/3”)
125    pub fn protocol(&self) -> Option<String> {
126        get_property_string("request.protocol")
127    }
128
129    /// The query portion of the URL in the format of “name1=value1&name2=value2”.
130    pub fn query(&self) -> Option<String> {
131        get_property_string("request.query")
132    }
133
134    /// Total duration of the request
135    /// Available in HTTP filters after a request is complete.
136    pub fn duration(&self) -> Option<Duration> {
137        let raw = get_property_decode::<prost_types::Duration>("request.duration")?;
138        if raw.seconds < 0 || raw.nanos < 0 {
139            warn!("request.duration returned a negative duration, skipped");
140            None
141        } else {
142            Some(Duration::new(raw.seconds as u64, raw.nanos as u32))
143        }
144    }
145
146    /// Size of the request body. Content length header is used if available.
147    /// Available in HTTP filters after a request is complete.
148    pub fn size(&self) -> Option<usize> {
149        get_property_int("request.size").map(|x| x as usize)
150    }
151
152    /// Total size of the request including the approximate uncompressed size of the headers
153    /// Available in HTTP filters after a request is complete.
154    pub fn total_size(&self) -> Option<usize> {
155        get_property_int("request.total_size").map(|x| x as usize)
156    }
157}
158
159/// Available in HTTP filters during a response.
160pub struct ResponseAttributes(());
161
162impl ResponseAttributes {
163    /// Response HTTP status code
164    pub fn code(&self) -> Option<u32> {
165        get_property_int("response.code").map(|x| x as u32)
166    }
167
168    /// Internal response code details (subject to change)
169    pub fn code_details(&self) -> Option<String> {
170        get_property_string("response.code_details")
171    }
172
173    /// Additional details about the response beyond the standard response code encoded as a bit-vector
174    pub fn flags(&self) -> Option<u64> {
175        get_property_int("response.flags").map(|x| x as u64)
176    }
177
178    /// Response gRPC status code
179    pub fn grpc_status(&self) -> Option<u32> {
180        get_property_int("response.grpc_status").map(|x| x as u32)
181    }
182
183    /// All response headers indexed by the lower-cased header name
184    /// Header values in response.headers associative array are comma-concatenated in case of multiple values.
185    pub fn headers(&self) -> Option<Vec<(String, Vec<u8>)>> {
186        let headers = get_property_decode::<attributes_proto::StringMap>("response.headers")?;
187        Some(headers.map.into_iter().map(|x| (x.key, x.value)).collect())
188    }
189
190    /// All response trailers indexed by the lower-cased trailer name
191    /// Header values in response.trailers associative array are comma-concatenated in case of multiple values.
192    pub fn trailers(&self) -> Option<Vec<(String, Vec<u8>)>> {
193        let headers = get_property_decode::<attributes_proto::StringMap>("response.trailers")?;
194        Some(headers.map.into_iter().map(|x| (x.key, x.value)).collect())
195    }
196
197    /// The path portion of the URL without the query string
198    pub fn size(&self) -> Option<usize> {
199        get_property_int("response.size").map(|x| x as usize)
200    }
201
202    /// Total size of the response including the approximate uncompressed size of the headers and the trailers
203    pub fn total_size(&self) -> Option<usize> {
204        get_property_int("response.total_size").map(|x| x as usize)
205    }
206}
207
208/// The following attributes are available once the downstream connection is established
209pub struct ConnectionAttributes(());
210
211impl ConnectionAttributes {
212    /// Downstream connection remote address & port
213    pub fn source_address(&self) -> Option<SocketAddr> {
214        get_property_string("source.address").and_then(|x| x.parse().ok())
215    }
216
217    /// Downstream connection remote port
218    pub fn source_port(&self) -> Option<u16> {
219        get_property_int("source.port").map(|x| x as u16)
220    }
221
222    /// Downstream connection local address & port
223    pub fn destination_address(&self) -> Option<SocketAddr> {
224        get_property_string("destination.address").and_then(|x| x.parse().ok())
225    }
226
227    /// Downstream connection local port
228    pub fn destination_port(&self) -> Option<u16> {
229        get_property_int("destination.port").map(|x| x as u16)
230    }
231
232    /// Downstream connection ID
233    pub fn id(&self) -> Option<u64> {
234        get_property_int("connection.id").map(|x| x as u64)
235    }
236
237    /// Indicates whether TLS is applied to the downstream connection and the peer certificate is presented
238    pub fn mtls(&self) -> Option<bool> {
239        get_property_bool("connection.mtls")
240    }
241
242    /// Requested server name in the downstream TLS connection
243    pub fn requested_server_name(&self) -> Option<String> {
244        get_property_string("connection.requested_server_name")
245    }
246
247    /// Requested server name in the downstream TLS connection
248    pub fn tls_version(&self) -> Option<String> {
249        get_property_string("connection.tls_version")
250    }
251
252    /// Requested server name in the downstream TLS connection
253    pub fn subject_local_certificate(&self) -> Option<String> {
254        get_property_string("connection.subject_local_certificate")
255    }
256
257    /// Requested server name in the downstream TLS connection
258    pub fn subject_peer_certificate(&self) -> Option<String> {
259        get_property_string("connection.subject_peer_certificate")
260    }
261
262    /// Requested server name in the downstream TLS connection
263    pub fn dns_san_local_certificate(&self) -> Option<String> {
264        get_property_string("connection.dns_san_local_certificate")
265    }
266
267    /// Requested server name in the downstream TLS connection
268    pub fn dns_san_peer_certificate(&self) -> Option<String> {
269        get_property_string("connection.dns_san_peer_certificate")
270    }
271
272    /// Requested server name in the downstream TLS connection
273    pub fn uri_san_local_certificate(&self) -> Option<String> {
274        get_property_string("connection.uri_san_local_certificate")
275    }
276
277    /// Requested server name in the downstream TLS connection
278    pub fn uri_san_peer_certificate(&self) -> Option<String> {
279        get_property_string("connection.uri_san_peer_certificate")
280    }
281
282    /// Requested server name in the downstream TLS connection
283    pub fn sha256_peer_certificate_digest(&self) -> Option<String> {
284        get_property_string("connection.sha256_peer_certificate_digest")
285    }
286
287    /// The following additional attributes are available upon the downstream connection termination:
288    /// Internal termination details of the connection (subject to change)
289    pub fn termination_details(&self) -> Option<String> {
290        get_property_string("connection.termination_details")
291    }
292}
293
294/// The following attributes are available once the upstream connection is established
295pub struct UpstreamAttributes(());
296
297impl UpstreamAttributes {
298    /// Upstream connection remote address & port
299    pub fn address(&self) -> Option<SocketAddr> {
300        get_property_string("upstream.address").and_then(|x| x.parse().ok())
301    }
302
303    /// Upstream connection remote port
304    pub fn port(&self) -> Option<u16> {
305        get_property_int("upstream.port").map(|x| x as u16)
306    }
307
308    /// TLS version of the upstream TLS connection
309    pub fn tls_version(&self) -> Option<String> {
310        get_property_string("upstream.tls_version")
311    }
312
313    /// The subject field of the local certificate in the upstream TLS connection
314    pub fn subject_local_certificate(&self) -> Option<String> {
315        get_property_string("upstream.subject_local_certificate")
316    }
317
318    /// The subject field of the local certificate in the upstream TLS connection
319    pub fn subject_peer_certificate(&self) -> Option<String> {
320        get_property_string("upstream.subject_peer_certificate")
321    }
322
323    /// The first DNS entry in the SAN field of the local certificate in the upstream TLS connection
324    pub fn dns_san_local_certificate(&self) -> Option<String> {
325        get_property_string("upstream.dns_san_local_certificate")
326    }
327
328    /// The first DNS entry in the SAN field of the peer certificate in the upstream TLS connection
329    pub fn dns_san_peer_certificate(&self) -> Option<String> {
330        get_property_string("upstream.dns_san_peer_certificate")
331    }
332
333    /// The first URI entry in the SAN field of the local certificate in the upstream TLS connection
334    pub fn uri_san_local_certificate(&self) -> Option<String> {
335        get_property_string("upstream.uri_san_local_certificate")
336    }
337
338    /// The first URI entry in the SAN field of the peer certificate in the upstream TLS connection
339    pub fn uri_san_peer_certificate(&self) -> Option<String> {
340        get_property_string("upstream.uri_san_peer_certificate")
341    }
342
343    /// Requested server name in the downstream TLS connection
344    pub fn sha256_peer_certificate_digest(&self) -> Option<String> {
345        get_property_string("upstream.sha256_peer_certificate_digest")
346    }
347
348    /// The local address of the upstream connection
349    pub fn local_address(&self) -> Option<String> {
350        get_property_string("upstream.local_address")
351    }
352
353    /// The upstream transport failure reason e.g. certificate validation failed
354    pub fn transport_failure_reason(&self) -> Option<String> {
355        get_property_string("upstream.transport_failure_reason")
356    }
357}
358
359/// Data exchanged between filters is available as the following attributes
360/// Note that these attributes may change during the life of a request as the data can be updated by filters at any point.
361pub struct MetadataAttributes(());
362
363impl MetadataAttributes {
364    /// Upstream connection remote address
365    pub fn metadata(&self) -> Option<Metadata> {
366        get_property_decode("metadata")
367    }
368
369    /// Mapping from a filter state name to its serialized string value
370    pub fn filter_state(&self) -> Option<Vec<(String, Vec<u8>)>> {
371        let headers = get_property_decode::<attributes_proto::StringMap>("filter_state")?;
372        Some(headers.map.into_iter().map(|x| (x.key, x.value)).collect())
373    }
374}
375
376/// Configuration identifiers and metadata related to the handling of the request or the connection is available as the following attributes
377pub struct ConfigurationAttributes(());
378
379impl ConfigurationAttributes {
380    /// Upstream cluster name
381    pub fn cluster_name(&self) -> Option<String> {
382        get_property_string("xds.cluster_name")
383    }
384
385    /// Upstream cluster metadata
386    pub fn cluster_metadata(&self) -> Option<Metadata> {
387        get_property_decode("xds.cluster_metadata")
388    }
389
390    /// Route name
391    pub fn route_name(&self) -> Option<String> {
392        get_property_string("xds.route_name")
393    }
394
395    /// Route metadata
396    pub fn route_metadata(&self) -> Option<Metadata> {
397        get_property_decode("xds.route_metadata")
398    }
399
400    /// Upstream host metadata
401    pub fn upstream_host_metadata(&self) -> Option<Metadata> {
402        get_property_decode("xds.upstream_host_metadata")
403    }
404
405    /// Listener filter chain name
406    pub fn filter_chain_name(&self) -> Option<String> {
407        get_property_string("xds.filter_chain_name")
408    }
409}
410
411#[repr(i64)]
412#[derive(Debug)]
413pub enum ListenerDirection {
414    Unspecified = 0,
415    Inbound = 1,
416    Outbound = 2,
417}
418
419impl ListenerDirection {
420    pub fn from_i64(v: i64) -> Option<Self> {
421        match v {
422            0 => Some(ListenerDirection::Unspecified),
423            1 => Some(ListenerDirection::Inbound),
424            2 => Some(ListenerDirection::Outbound),
425            _ => None,
426        }
427    }
428}
429
430/// The following extra attributes are available to Wasm extensions
431pub struct WasmAttributes(());
432
433impl WasmAttributes {
434    pub fn get() -> Self {
435        Self(())
436    }
437
438    /// Plugin name
439    pub fn plugin_name(&self) -> Option<String> {
440        get_property_string("plugin_name")
441    }
442
443    /// Plugin root ID
444    pub fn plugin_root_id(&self) -> Option<String> {
445        get_property_string("plugin_root_id")
446    }
447
448    /// Plugin VM ID
449    pub fn plugin_vm_id(&self) -> Option<String> {
450        get_property_string("plugin_vm_id")
451    }
452
453    /// Local node description
454    pub fn node(&self) -> Option<Node> {
455        get_property_decode("node")
456    }
457
458    /// Upstream cluster name
459    pub fn cluster_name(&self) -> Option<String> {
460        get_property_string("cluster_name")
461    }
462
463    /// Upstream cluster metadata
464    pub fn cluster_metadata(&self) -> Option<Metadata> {
465        get_property_decode("cluster_metadata")
466    }
467
468    /// Enumeration value of the listener traffic direction
469    pub fn listener_direction(&self) -> Option<ListenerDirection> {
470        get_property_int("listener_direction").and_then(ListenerDirection::from_i64)
471    }
472
473    /// Listener metadata
474    pub fn listener_metadata(&self) -> Option<Metadata> {
475        get_property_decode("listener_metadata")
476    }
477
478    /// Route name
479    pub fn route_name(&self) -> Option<String> {
480        get_property_string("route_name")
481    }
482
483    /// Route metadata
484    pub fn route_metadata(&self) -> Option<Metadata> {
485        get_property_decode("route_metadata")
486    }
487
488    /// Upstream host metadata
489    pub fn upstream_host_metadata(&self) -> Option<Metadata> {
490        get_property_decode("upstream_host_metadata")
491    }
492}