1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! # Binary Propagator
//!
//! `BinaryFormat` is a formatter to serialize and deserialize a
//! value into a binary format.
//!
//! `BinaryFormat` MUST expose the APIs that serializes values into bytes,
//! and deserializes values from bytes.
use crate::api;
use crate::api::trace::TraceState;
use std::convert::TryInto;

/// Used to serialize and deserialize `SpanReference`s to and from a binary
/// representation.
pub trait BinaryFormat {
    /// Serializes span context into a byte array and returns the array.
    fn to_bytes(&self, context: &api::trace::SpanReference) -> [u8; 29];

    /// Deserializes a span context from a byte array.
    fn from_bytes(&self, bytes: Vec<u8>) -> api::trace::SpanReference;
}

/// Extracts and injects `SpanReference`s from byte arrays.
#[derive(Debug, Default)]
pub struct BinaryPropagator {}

impl BinaryPropagator {
    /// Create a new binary propagator.
    pub fn new() -> Self {
        BinaryPropagator {}
    }
}

impl BinaryFormat for BinaryPropagator {
    /// Serializes span context into a byte array and returns the array.
    fn to_bytes(&self, context: &api::trace::SpanReference) -> [u8; 29] {
        let mut res = [0u8; 29];
        if !context.is_valid() {
            return res;
        }
        res[2..18].copy_from_slice(&context.trace_id().to_u128().to_be_bytes());
        res[18] = 1;
        res[19..27].copy_from_slice(&context.span_id().to_u64().to_be_bytes());
        res[27] = 2;
        res[28] = context.trace_flags();

        res
    }

    /// Deserializes a span context from a byte array.
    fn from_bytes(&self, bytes: Vec<u8>) -> api::trace::SpanReference {
        if bytes.is_empty() {
            return api::trace::SpanReference::empty_context();
        }
        let trace_id: u128;
        let mut span_id = 0;
        let mut trace_flags = 0;
        let mut b = &bytes[1..];
        if b.len() >= 17 && b[0] == 0 {
            trace_id = u128::from_be_bytes(b[1..17].try_into().unwrap());
            b = &b[17..];
        } else {
            return api::trace::SpanReference::empty_context();
        }
        if b.len() >= 9 && b[0] == 1 {
            span_id = u64::from_be_bytes(b[1..9].try_into().unwrap());
            b = &b[9..];
        }
        if b.len() >= 2 && b[0] == 2 {
            trace_flags = b[1]
        }

        let span_reference = api::trace::SpanReference::new(
            api::trace::TraceId::from_u128(trace_id),
            api::trace::SpanId::from_u64(span_id),
            trace_flags,
            true,
            // TODO traceparent and tracestate should both begin with a 0 byte, figure out how to differentiate
            TraceState::default(),
        );

        if span_reference.is_valid() {
            span_reference
        } else {
            api::trace::SpanReference::empty_context()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::api::trace::TraceState;

    #[rustfmt::skip]
    fn to_bytes_data() -> Vec<(api::trace::SpanReference, [u8; 29])> {
        vec![
            // Context with sampled
            (api::trace::SpanReference::new(
                api::trace::TraceId::from_u128(0x4bf9_2f35_77b3_4da6_a3ce_929d_0e0e_4736),
                api::trace::SpanId::from_u64(0x00f0_67aa_0ba9_02b7), 1, true, TraceState::default()), [
                0x00, 0x00, 0x4b, 0xf9, 0x2f, 0x35, 0x77, 0xb3, 0x4d, 0xa6, 0xa3, 0xce, 0x92, 0x9d, 0x0e, 0x0e, 0x47, 0x36,
                0x01, 0x00, 0xf0, 0x67, 0xaa, 0x0b, 0xa9, 0x02, 0xb7,
                0x02, 0x01,
            ]),
            // Context without sampled
            (api::trace::SpanReference::new(
                api::trace::TraceId::from_u128(0x4bf9_2f35_77b3_4da6_a3ce_929d_0e0e_4736),
                api::trace::SpanId::from_u64(0x00f0_67aa_0ba9_02b7), 0, true, TraceState::default()), [
                0x00, 0x00, 0x4b, 0xf9, 0x2f, 0x35, 0x77, 0xb3, 0x4d, 0xa6, 0xa3, 0xce, 0x92, 0x9d, 0x0e, 0x0e, 0x47, 0x36,
                0x01, 0x00, 0xf0, 0x67, 0xaa, 0x0b, 0xa9, 0x02, 0xb7,
                0x02, 0x00,
            ]),
            // Invalid context
            (api::trace::SpanReference::empty_context(), [0u8; 29]),
        ]
    }

    #[rustfmt::skip]
    fn from_bytes_data() -> Vec<(api::trace::SpanReference, Vec<u8>)> {
        vec![
            // Future version of the proto
            (api::trace::SpanReference::new(api::trace::TraceId::from_u128(0x4bf9_2f35_77b3_4da6_a3ce_929d_0e0e_4736), api::trace::SpanId::from_u64(0x00f0_67aa_0ba9_02b7), 1, true, TraceState::default()), vec![
                0x02, 0x00, 0x4b, 0xf9, 0x2f, 0x35, 0x77, 0xb3, 0x4d, 0xa6, 0xa3, 0xce, 0x92, 0x9d, 0x0e, 0x0e, 0x47, 0x36,
                0x01, 0x00, 0xf0, 0x67, 0xaa, 0x0b, 0xa9, 0x02, 0xb7,
                0x02, 0x01,
            ]),
            // current version with sampled
            (api::trace::SpanReference::new(api::trace::TraceId::from_u128(0x4bf9_2f35_77b3_4da6_a3ce_929d_0e0e_4736), api::trace::SpanId::from_u64(0x00f0_67aa_0ba9_02b7), 1, true, TraceState::default()), vec![
                0x02, 0x00, 0x4b, 0xf9, 0x2f, 0x35, 0x77, 0xb3, 0x4d, 0xa6, 0xa3, 0xce, 0x92, 0x9d, 0x0e, 0x0e, 0x47, 0x36,
                0x01, 0x00, 0xf0, 0x67, 0xaa, 0x0b, 0xa9, 0x02, 0xb7,
                0x02, 0x01,
            ]),
            // valid context without option
            (api::trace::SpanReference::new(api::trace::TraceId::from_u128(0x4bf9_2f35_77b3_4da6_a3ce_929d_0e0e_4736), api::trace::SpanId::from_u64(0x00f0_67aa_0ba9_02b7), 0, true, TraceState::default()), vec![
                0x00, 0x00, 0x4b, 0xf9, 0x2f, 0x35, 0x77, 0xb3, 0x4d, 0xa6, 0xa3, 0xce, 0x92, 0x9d, 0x0e, 0x0e, 0x47, 0x36,
                0x01, 0x00, 0xf0, 0x67, 0xaa, 0x0b, 0xa9, 0x02, 0xb7,
            ]),
            // zero trace id
            (api::trace::SpanReference::empty_context(), vec![
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x02, 0x01,
            ]),
            // zero span id
            (api::trace::SpanReference::empty_context(), vec![
                0x00, 0x00, 0x4b, 0xf9, 0x2f, 0x35, 0x77, 0xb3, 0x4d, 0xa6, 0xa3, 0xce, 0x92, 0x9d, 0x0e, 0x0e, 0x47, 0x36,
                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x02, 0x01,
            ]),
            // wrong trace id field number
            (api::trace::SpanReference::empty_context(), vec![
                0x00, 0x01, 0x4b, 0xf9, 0x2f, 0x35, 0x77, 0xb3, 0x4d, 0xa6, 0xa3, 0xce, 0x92, 0x9d, 0x0e, 0x0e, 0x47, 0x36,
                0x01, 0x00, 0xf0, 0x67, 0xaa, 0x0b, 0xa9, 0x02, 0xb7,
            ]),
            // short byte array
            (api::trace::SpanReference::empty_context(), vec![
                0x00, 0x00, 0x4b, 0xf9, 0x2f, 0x35, 0x77, 0xb3, 0x4d,
            ]),
        ]
    }

    #[test]
    fn to_bytes_conversion() {
        let propagator = BinaryPropagator::new();

        for (context, data) in to_bytes_data() {
            assert_eq!(propagator.to_bytes(&context), data)
        }
    }

    #[test]
    fn from_bytes_conversion() {
        let propagator = BinaryPropagator::new();

        for (context, data) in from_bytes_data() {
            assert_eq!(propagator.from_bytes(data), context)
        }
    }
}