pdk_script/bindings/
attributes.rs1use crate::constants::{
3 DESTINATION_ADDRESS, METHOD_HEADER, PATH_HEADER, REQUEST_PROTOCOL, REQUEST_SCHEME,
4 SCHEME_HEADER, SOURCE_ADDRESS, STATUS_CODE_HEADER,
5};
6use classy::event::HeadersAccessor;
7use classy::hl::{HeadersHandler, HttpClientResponse, PropertyAccessor};
8use pel::runtime::value::Value as PelValue;
9use std::collections::HashMap;
10use url::Url;
11
12pub trait AttributesBinding {
14 fn extract_headers(&self) -> HashMap<String, String>;
16
17 fn extract_header(&self, key: &str) -> Option<String>;
19
20 fn extract_query_params(&self) -> HashMap<String, String> {
22 self.extract_header(PATH_HEADER)
23 .and_then(fake_url)
24 .map(|url| {
25 url.query_pairs()
26 .map(|(k, v)| (k.to_string(), v.to_string()))
27 .collect()
28 })
29 .unwrap_or_default()
30 }
31
32 fn method(&self) -> Option<String> {
34 self.extract_header(METHOD_HEADER)
35 }
36
37 fn path(&self) -> Option<String> {
39 let mut url = self.extract_header(PATH_HEADER).and_then(fake_url)?;
40 url.set_query(None);
41 Some(url.path().to_string())
42 }
43
44 fn uri(&self) -> Option<String> {
46 self.extract_header(PATH_HEADER)
47 }
48
49 fn remote_address(&self) -> Option<String> {
51 None
52 }
53
54 fn local_address(&self) -> Option<String> {
56 None
57 }
58
59 fn query_string(&self) -> Option<String> {
61 let path = self.extract_header(PATH_HEADER)?;
62 fake_url(path)?.query().map(str::to_string)
63 }
64
65 fn scheme(&self) -> Option<String> {
67 self.extract_header(SCHEME_HEADER)
68 }
69
70 fn version(&self) -> Option<String> {
72 None
73 }
74
75 fn status_code(&self) -> Option<u32> {
77 self.extract_header(STATUS_CODE_HEADER)
78 .and_then(|value| value.parse::<u32>().ok())
79 }
80}
81
82impl AttributesBinding for HttpClientResponse {
83 fn extract_headers(&self) -> HashMap<String, String> {
84 self.headers().clone()
85 }
86
87 fn extract_header(&self, key: &str) -> Option<String> {
88 self.header(key).cloned()
89 }
90}
91
92pub struct HandlerAttributesBinding<'a> {
93 handler: &'a dyn HeadersHandler,
94 properties: Option<&'a dyn PropertyAccessor>,
95}
96
97impl<'a> HandlerAttributesBinding<'a> {
98 pub fn new(handler: &'a dyn HeadersHandler, properties: &'a dyn PropertyAccessor) -> Self {
99 Self {
100 handler,
101 properties: Some(properties),
102 }
103 }
104
105 pub fn partial(handler: &'a dyn HeadersHandler) -> Self {
107 Self {
108 handler,
109 properties: None,
110 }
111 }
112}
113
114impl AttributesBinding for HandlerAttributesBinding<'_> {
115 fn extract_headers(&self) -> HashMap<String, String> {
116 self.handler.headers().into_iter().collect()
117 }
118
119 fn extract_header(&self, key: &str) -> Option<String> {
120 self.handler.header(key)
121 }
122
123 fn remote_address(&self) -> Option<String> {
124 self.properties.and_then(remote_address)
125 }
126
127 fn local_address(&self) -> Option<String> {
128 self.properties.and_then(local_address)
129 }
130
131 fn scheme(&self) -> Option<String> {
132 self.properties.and_then(scheme)
133 }
134
135 fn version(&self) -> Option<String> {
136 self.properties.and_then(version)
137 }
138}
139
140pub struct AccessorAttributesBinding<'a> {
141 accessor: &'a dyn HeadersAccessor,
142 properties: Option<&'a dyn PropertyAccessor>,
143}
144
145impl<'a> AccessorAttributesBinding<'a> {
146 pub fn new(handler: &'a dyn HeadersAccessor, properties: &'a dyn PropertyAccessor) -> Self {
147 Self {
148 accessor: handler,
149 properties: Some(properties),
150 }
151 }
152
153 pub fn partial(accessor: &'a dyn HeadersAccessor) -> Self {
155 Self {
156 accessor,
157 properties: None,
158 }
159 }
160}
161
162impl AttributesBinding for AccessorAttributesBinding<'_> {
163 fn extract_headers(&self) -> HashMap<String, String> {
164 self.accessor.headers().into_iter().collect()
165 }
166
167 fn extract_header(&self, key: &str) -> Option<String> {
168 self.accessor.header(key)
169 }
170
171 fn remote_address(&self) -> Option<String> {
172 self.properties.and_then(remote_address)
173 }
174
175 fn local_address(&self) -> Option<String> {
176 self.properties.and_then(local_address)
177 }
178
179 fn scheme(&self) -> Option<String> {
180 self.properties.and_then(scheme)
181 }
182
183 fn version(&self) -> Option<String> {
184 self.properties.and_then(version)
185 }
186}
187
188pub(crate) struct AttributesBindingAdapter<'a> {
189 delegate: &'a dyn AttributesBinding,
190}
191
192impl<'a> AttributesBindingAdapter<'a> {
193 pub fn new(delegate: &'a dyn AttributesBinding) -> Self {
194 Self { delegate }
195 }
196
197 pub fn extract_headers(&self) -> HashMap<String, PelValue> {
198 convert_maps(self.delegate.extract_headers())
199 }
200
201 pub fn extract_header(&self, key: &str) -> Option<PelValue> {
202 convert_string(self.delegate.extract_header(key))
203 }
204
205 pub fn extract_query_params(&self) -> HashMap<String, PelValue> {
206 convert_maps(self.delegate.extract_query_params())
207 }
208
209 pub fn method(&self) -> Option<PelValue> {
210 convert_string(self.delegate.method())
211 }
212
213 pub fn path(&self) -> Option<PelValue> {
214 convert_string(self.delegate.path())
215 }
216
217 pub fn uri(&self) -> Option<PelValue> {
218 convert_string(self.delegate.uri())
219 }
220
221 pub fn remote_address(&self) -> Option<PelValue> {
222 convert_string(self.delegate.remote_address())
223 }
224
225 pub fn local_address(&self) -> Option<PelValue> {
226 convert_string(self.delegate.local_address())
227 }
228
229 pub fn query_string(&self) -> Option<PelValue> {
230 convert_string(self.delegate.query_string())
231 }
232
233 pub fn scheme(&self) -> Option<PelValue> {
234 convert_string(self.delegate.scheme())
235 }
236
237 pub fn version(&self) -> Option<PelValue> {
238 convert_string(self.delegate.version())
239 }
240
241 pub fn status_code(&self) -> Option<PelValue> {
242 self.delegate
243 .status_code()
244 .map(|s| PelValue::number(s as f64))
245 }
246}
247
248fn fake_url(uri: String) -> Option<Url> {
249 Url::parse("http://fake_base").ok()?.join(uri.as_str()).ok()
250}
251fn convert_maps(map: HashMap<String, String>) -> HashMap<String, PelValue> {
252 map.into_iter()
253 .map(|(key, val)| (key, PelValue::string(val)))
254 .collect()
255}
256
257fn convert_string(val: Option<String>) -> Option<PelValue> {
258 val.map(PelValue::string)
259}
260
261fn remote_address(properties: &dyn PropertyAccessor) -> Option<String> {
262 properties
263 .read_property(SOURCE_ADDRESS)
264 .map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
265}
266
267fn local_address(properties: &dyn PropertyAccessor) -> Option<String> {
268 properties
269 .read_property(DESTINATION_ADDRESS)
270 .map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
271}
272
273fn scheme(properties: &dyn PropertyAccessor) -> Option<String> {
274 properties
275 .read_property(REQUEST_SCHEME)
276 .map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
277}
278
279fn version(properties: &dyn PropertyAccessor) -> Option<String> {
280 properties
281 .read_property(REQUEST_PROTOCOL)
282 .map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
283}