1use crate::{
44 baggage::{BaggageExt, KeyValueMetadata},
45 propagation::{text_map_propagator::FieldIter, Extractor, Injector, TextMapPropagator},
46 Context,
47};
48use percent_encoding::{percent_decode_str, utf8_percent_encode, AsciiSet, CONTROLS};
49use std::iter;
50
51static BAGGAGE_HEADER: &str = "baggage";
52const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b';').add(b',').add(b'=');
53
54lazy_static::lazy_static! {
55 static ref BAGGAGE_FIELDS: [String; 1] = [BAGGAGE_HEADER.to_string()];
56}
57
58#[derive(Debug, Default)]
101pub struct BaggagePropagator {
102 _private: (),
103}
104
105impl BaggagePropagator {
106 pub fn new() -> Self {
108 BaggagePropagator { _private: () }
109 }
110}
111
112impl TextMapPropagator for BaggagePropagator {
113 fn inject_context(&self, cx: &Context, injector: &mut dyn Injector) {
115 let baggage = cx.baggage();
116 if !baggage.is_empty() {
117 let header_value = baggage
118 .iter()
119 .map(|(name, (value, metadata))| {
120 let metadata_str = metadata.as_str().trim();
121 let metadata_prefix = if metadata_str.is_empty() { "" } else { ";" };
122 utf8_percent_encode(name.as_str().trim(), FRAGMENT)
123 .chain(iter::once("="))
124 .chain(utf8_percent_encode(value.as_str().trim(), FRAGMENT))
125 .chain(iter::once(metadata_prefix))
126 .chain(iter::once(metadata_str))
127 .collect()
128 })
129 .collect::<Vec<String>>()
130 .join(",");
131 injector.set(BAGGAGE_HEADER, header_value);
132 }
133 }
134
135 fn extract_with_context(&self, cx: &Context, extractor: &dyn Extractor) -> Context {
137 if let Some(header_value) = extractor.get(BAGGAGE_HEADER) {
138 let baggage = header_value.split(',').flat_map(|context_value| {
139 if let Some((name_and_value, props)) = context_value
140 .split(';')
141 .collect::<Vec<&str>>()
142 .split_first()
143 {
144 let mut iter = name_and_value.split('=');
145 if let (Some(name), Some(value)) = (iter.next(), iter.next()) {
146 let name = percent_decode_str(name).decode_utf8().map_err(|_| ())?;
147 let value = percent_decode_str(value).decode_utf8().map_err(|_| ())?;
148
149 let decoded_props = props
152 .iter()
153 .flat_map(|prop| percent_decode_str(prop).decode_utf8())
154 .map(|prop| prop.trim().to_string())
155 .collect::<Vec<String>>()
156 .join(";"); Ok(KeyValueMetadata::new(
159 name.trim().to_owned(),
160 value.trim().to_string(),
161 decoded_props.as_str(),
162 ))
163 } else {
164 Err(())
166 }
167 } else {
168 Err(())
170 }
171 });
172 cx.with_baggage(baggage)
173 } else {
174 cx.clone()
175 }
176 }
177
178 fn fields(&self) -> FieldIter<'_> {
179 FieldIter::new(BAGGAGE_FIELDS.as_ref())
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186 use crate::{baggage::BaggageMetadata, propagation::TextMapPropagator, Key, KeyValue, Value};
187 use std::borrow::Cow;
188 use std::collections::HashMap;
189
190 #[rustfmt::skip]
191 fn valid_extract_data() -> Vec<(&'static str, HashMap<Key, Value>)> {
192 vec![
193 ("key1=val1,key2=val2", vec![(Key::new("key1"), Value::from("val1")), (Key::new("key2"), Value::from("val2"))].into_iter().collect()),
195 ("key1 = val1, key2 =val2 ", vec![(Key::new("key1"), Value::from("val1")), (Key::new("key2"), Value::from("val2"))].into_iter().collect()),
197 ("key1=val1,key2=val2%2Cval3", vec![(Key::new("key1"), Value::from("val1")), (Key::new("key2"), Value::from("val2,val3"))].into_iter().collect()),
199 ("key1=val1,key2=val2,a,val3", vec![(Key::new("key1"), Value::from("val1")), (Key::new("key2"), Value::from("val2"))].into_iter().collect()),
201 ("key1=,key2=val2", vec![(Key::new("key1"), Value::from("")), (Key::new("key2"), Value::from("val2"))].into_iter().collect()),
203 ]
204 }
205
206 #[rustfmt::skip]
207 #[allow(clippy::type_complexity)]
208 fn valid_extract_data_with_metadata() -> Vec<(&'static str, HashMap<Key, (Value, BaggageMetadata)>)> {
209 vec![
210 ("key1=val1,key2=val2;prop=1", vec![(Key::new("key1"), (Value::from("val1"), BaggageMetadata::default())), (Key::new("key2"), (Value::from("val2"), BaggageMetadata::from("prop=1")))].into_iter().collect()),
212 ("key1=val1,key2=val2;prop1", vec![(Key::new("key1"), (Value::from("val1"), BaggageMetadata::default())), (Key::new("key2"), (Value::from("val2"), BaggageMetadata::from("prop1")))].into_iter().collect()),
214 ("key1=value1;property1;property2, key2 = value2, key3=value3; propertyKey=propertyValue",
215 vec![
216 (Key::new("key1"), (Value::from("value1"), BaggageMetadata::from("property1;property2"))),
217 (Key::new("key2"), (Value::from("value2"), BaggageMetadata::default())),
218 (Key::new("key3"), (Value::from("value3"), BaggageMetadata::from("propertyKey=propertyValue")))
219 ].into_iter().collect()),
220 ]
221 }
222
223 #[rustfmt::skip]
224 fn valid_inject_data() -> Vec<(Vec<KeyValue>, Vec<&'static str>)> {
225 vec![
226 (vec![KeyValue::new("key1", "val1"), KeyValue::new("key2", "val2")], vec!["key1=val1", "key2=val2"]),
228 (vec![KeyValue::new("key1", "val1,val2"), KeyValue::new("key2", "val3=4")], vec!["key1=val1%2Cval2", "key2=val3%3D4"]),
230 (
232 vec![
233 KeyValue::new("key1", true),
234 KeyValue::new("key2", Value::I64(123)),
235 KeyValue::new("key3", Value::F64(123.567)),
236 ],
237 vec![
238 "key1=true",
239 "key2=123",
240 "key3=123.567",
241 ],
242 ),
243 (
245 vec![
246 KeyValue::new("key1", Value::Array(vec![true, false].into())),
247 KeyValue::new("key2", Value::Array(vec![123, 456].into())),
248 KeyValue::new("key3", Value::Array(vec![Cow::from("val1"), Cow::from("val2")].into())),
249 ],
250 vec![
251 "key1=[true%2Cfalse]",
252 "key2=[123%2C456]",
253 "key3=[%22val1%22%2C%22val2%22]",
254 ],
255 ),
256 ]
257 }
258
259 #[rustfmt::skip]
260 fn valid_inject_data_metadata() -> Vec<(Vec<KeyValueMetadata>, Vec<&'static str>)> {
261 vec![
262 (
263 vec![
264 KeyValueMetadata::new("key1", "val1", "prop1"),
265 KeyValue::new("key2", "val2").into(),
266 KeyValueMetadata::new("key3", "val3", "anykey=anyvalue")
267 ],
268 vec![
269 "key1=val1;prop1",
270 "key2=val2",
271 "key3=val3;anykey=anyvalue"
272 ],
273 )
274 ]
275 }
276
277 #[test]
278 fn extract_baggage() {
279 let propagator = BaggagePropagator::new();
280
281 for (header_value, kvs) in valid_extract_data() {
282 let mut extractor: HashMap<String, String> = HashMap::new();
283 extractor.insert(BAGGAGE_HEADER.to_string(), header_value.to_string());
284 let context = propagator.extract(&extractor);
285 let baggage = context.baggage();
286
287 assert_eq!(kvs.len(), baggage.len());
288 for (key, (value, _metadata)) in baggage {
289 assert_eq!(Some(value), kvs.get(key))
290 }
291 }
292 }
293
294 #[test]
295 fn inject_baggage() {
296 let propagator = BaggagePropagator::new();
297
298 for (kvm, header_parts) in valid_inject_data() {
299 let mut injector = HashMap::new();
300 let cx = Context::current_with_baggage(kvm);
301 propagator.inject_context(&cx, &mut injector);
302 let header_value = injector.get(BAGGAGE_HEADER).unwrap();
303 assert_eq!(header_parts.join(",").len(), header_value.len(),);
304 for header_part in &header_parts {
305 assert!(header_value.contains(header_part),)
306 }
307 }
308 }
309
310 #[test]
311 fn extract_baggage_with_metadata() {
312 let propagator = BaggagePropagator::new();
313 for (header_value, kvm) in valid_extract_data_with_metadata() {
314 let mut extractor: HashMap<String, String> = HashMap::new();
315 extractor.insert(BAGGAGE_HEADER.to_string(), header_value.to_string());
316 let context = propagator.extract(&extractor);
317 let baggage = context.baggage();
318
319 assert_eq!(kvm.len(), baggage.len());
320 for (key, value_and_prop) in baggage {
321 assert_eq!(Some(value_and_prop), kvm.get(key))
322 }
323 }
324 }
325
326 #[test]
327 fn inject_baggage_with_metadata() {
328 let propagator = BaggagePropagator::new();
329
330 for (kvm, header_parts) in valid_inject_data_metadata() {
331 let mut injector = HashMap::new();
332 let cx = Context::current_with_baggage(kvm);
333 propagator.inject_context(&cx, &mut injector);
334 let header_value = injector.get(BAGGAGE_HEADER).unwrap();
335
336 assert_eq!(header_parts.join(",").len(), header_value.len());
337 for header_part in &header_parts {
338 assert!(header_value.contains(header_part),)
339 }
340 }
341 }
342}