11use rand:: Rng ;
22use std:: fmt;
33
4- use crate :: Headers ;
4+ use crate :: headers:: { HeaderName , HeaderValue , Headers , TRACEPARENT } ;
5+ use crate :: Status ;
56
6- /// Extract and inject [Trace-Context](https://w3c.github.io/trace-context/) headers.
7+ /// Extract and apply [Trace-Context](https://w3c.github.io/trace-context/) headers.
78///
8- /// ## Examples
9+ /// # Examples
910///
1011/// ```
12+ /// # fn main() -> http_types::Result<()> {
13+ /// #
1114/// use http_types::trace::TraceContext;
1215///
1316/// let mut res = http_types::Response::new(200);
@@ -17,14 +20,16 @@ use crate::Headers;
1720/// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
1821/// );
1922///
20- /// let context = TraceContext::extract (&res).unwrap();
23+ /// let context = TraceContext::from_headers (&res)? .unwrap();
2124///
2225/// let trace_id = u128::from_str_radix("0af7651916cd43dd8448eb211c80319c", 16);
2326/// let parent_id = u64::from_str_radix("00f067aa0ba902b7", 16);
2427///
2528/// assert_eq!(context.trace_id(), trace_id.unwrap());
2629/// assert_eq!(context.parent_id(), parent_id.ok());
2730/// assert_eq!(context.sampled(), true);
31+ /// #
32+ /// # Ok(()) }
2833/// ```
2934#[ derive( Debug ) ]
3035pub struct TraceContext {
@@ -36,10 +41,44 @@ pub struct TraceContext {
3641}
3742
3843impl TraceContext {
44+ /// Generate a new TraceContect object without a parent.
45+ ///
46+ /// By default root TraceContext objects are sampled.
47+ /// To mark it unsampled, call `context.set_sampled(false)`.
48+ ///
49+ /// # Examples
50+ /// ```
51+ /// use http_types::trace::TraceContext;
52+ ///
53+ /// let context = TraceContext::new();
54+ ///
55+ /// assert_eq!(context.parent_id(), None);
56+ /// assert_eq!(context.sampled(), true);
57+ /// ```
58+ pub fn new ( ) -> Self {
59+ let mut rng = rand:: thread_rng ( ) ;
60+
61+ Self {
62+ id : rng. gen ( ) ,
63+ version : 0 ,
64+ trace_id : rng. gen ( ) ,
65+ parent_id : None ,
66+ flags : 1 ,
67+ }
68+ }
69+
3970 /// Create and return TraceContext object based on `traceparent` HTTP header.
4071 ///
41- /// ## Examples
72+ /// # Errors
73+ ///
74+ /// This function may error if the header is malformed. An error with a
75+ /// status code of `400: Bad Request` will be generated.
76+ ///
77+ /// # Examples
78+ ///
4279 /// ```
80+ /// # fn main() -> http_types::Result<()> {
81+ /// #
4382 /// use http_types::trace::TraceContext;
4483 ///
4584 /// let mut res = http_types::Response::new(200);
@@ -48,65 +87,43 @@ impl TraceContext {
4887 /// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
4988 /// );
5089 ///
51- /// let context = TraceContext::extract (&res).unwrap();
90+ /// let context = TraceContext::from_headers (&res)? .unwrap();
5291 ///
5392 /// let trace_id = u128::from_str_radix("0af7651916cd43dd8448eb211c80319c", 16);
5493 /// let parent_id = u64::from_str_radix("00f067aa0ba902b7", 16);
5594 ///
5695 /// assert_eq!(context.trace_id(), trace_id.unwrap());
5796 /// assert_eq!(context.parent_id(), parent_id.ok());
5897 /// assert_eq!(context.sampled(), true);
98+ /// #
99+ /// # Ok(()) }
59100 /// ```
60- pub fn extract ( headers : impl AsRef < Headers > ) -> crate :: Result < Self > {
101+ pub fn from_headers ( headers : impl AsRef < Headers > ) -> crate :: Result < Option < Self > > {
61102 let headers = headers. as_ref ( ) ;
62103 let mut rng = rand:: thread_rng ( ) ;
63104
64- let traceparent = match headers. get ( "traceparent" ) {
65- Some ( header) => header. as_str ( ) ,
66- None => return Ok ( Self :: new_root ( ) ) ,
105+ let traceparent = match headers. get ( TRACEPARENT ) {
106+ Some ( header) => header,
107+ None => return Ok ( None ) ,
67108 } ;
109+ let parts: Vec < & str > = traceparent. as_str ( ) . split ( '-' ) . collect ( ) ;
68110
69- let parts: Vec < & str > = traceparent. split ( '-' ) . collect ( ) ;
70-
71- Ok ( Self {
111+ Ok ( Some ( Self {
72112 id : rng. gen ( ) ,
73113 version : u8:: from_str_radix ( parts[ 0 ] , 16 ) ?,
74- trace_id : u128:: from_str_radix ( parts[ 1 ] , 16 ) ?,
75- parent_id : Some ( u64:: from_str_radix ( parts[ 2 ] , 16 ) ?) ,
76- flags : u8:: from_str_radix ( parts[ 3 ] , 16 ) ?,
77- } )
78- }
79-
80- /// Generate a new TraceContect object without a parent.
81- ///
82- /// By default root TraceContext objects are sampled.
83- /// To mark it unsampled, call `context.set_sampled(false)`.
84- ///
85- /// ## Examples
86- /// ```
87- /// use http_types::trace::TraceContext;
88- ///
89- /// let context = TraceContext::new_root();
90- ///
91- /// assert_eq!(context.parent_id(), None);
92- /// assert_eq!(context.sampled(), true);
93- /// ```
94- pub fn new_root ( ) -> Self {
95- let mut rng = rand:: thread_rng ( ) ;
96-
97- Self {
98- id : rng. gen ( ) ,
99- version : 0 ,
100- trace_id : rng. gen ( ) ,
101- parent_id : None ,
102- flags : 1 ,
103- }
114+ trace_id : u128:: from_str_radix ( parts[ 1 ] , 16 ) . status ( 400 ) ?,
115+ parent_id : Some ( u64:: from_str_radix ( parts[ 2 ] , 16 ) . status ( 400 ) ?) ,
116+ flags : u8:: from_str_radix ( parts[ 3 ] , 16 ) . status ( 400 ) ?,
117+ } ) )
104118 }
105119
106120 /// Add the traceparent header to the http headers
107121 ///
108- /// ## Examples
122+ /// # Examples
123+ ///
109124 /// ```
125+ /// # fn main() -> http_types::Result<()> {
126+ /// #
110127 /// use http_types::trace::TraceContext;
111128 /// use http_types::{Request, Response, Url, Method};
112129 ///
@@ -116,20 +133,33 @@ impl TraceContext {
116133 /// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
117134 /// );
118135 ///
119- /// let parent = TraceContext::extract (&req).unwrap();
136+ /// let parent = TraceContext::from_headers (&req)? .unwrap();
120137 ///
121138 /// let mut res = Response::new(200);
122- /// parent.inject (&mut res);
139+ /// parent.apply (&mut res);
123140 ///
124- /// let child = TraceContext::extract (&res).unwrap();
141+ /// let child = TraceContext::from_headers (&res)? .unwrap();
125142 ///
126143 /// assert_eq!(child.version(), parent.version());
127144 /// assert_eq!(child.trace_id(), parent.trace_id());
128145 /// assert_eq!(child.parent_id(), Some(parent.id()));
146+ /// #
147+ /// # Ok(()) }
129148 /// ```
130- pub fn inject ( & self , mut headers : impl AsMut < Headers > ) {
149+ pub fn apply ( & self , mut headers : impl AsMut < Headers > ) {
131150 let headers = headers. as_mut ( ) ;
132- headers. insert ( "traceparent" , format ! ( "{}" , self ) ) ;
151+ headers. insert ( TRACEPARENT , self . value ( ) ) ;
152+ }
153+
154+ /// Get the `HeaderName`.
155+ pub fn name ( & self ) -> HeaderName {
156+ TRACEPARENT
157+ }
158+
159+ /// Get the `HeaderValue`.
160+ pub fn value ( & self ) -> HeaderValue {
161+ let output = format ! ( "{}" , self ) ;
162+ unsafe { HeaderValue :: from_bytes_unchecked ( output. into ( ) ) }
133163 }
134164
135165 /// Generate a child of the current TraceContext and return it.
@@ -175,29 +205,33 @@ impl TraceContext {
175205
176206 /// Returns true if the trace is sampled
177207 ///
178- /// ## Examples
208+ /// # Examples
179209 ///
180210 /// ```
211+ /// # fn main() -> http_types::Result<()> {
212+ /// #
181213 /// use http_types::trace::TraceContext;
182214 /// use http_types::Response;
183215 ///
184216 /// let mut res = Response::new(200);
185217 /// res.insert_header("traceparent", "00-00000000000000000000000000000001-0000000000000002-01");
186- /// let context = TraceContext::extract (&res).unwrap();
218+ /// let context = TraceContext::from_headers (&res)? .unwrap();
187219 /// assert_eq!(context.sampled(), true);
220+ /// #
221+ /// # Ok(()) }
188222 /// ```
189223 pub fn sampled ( & self ) -> bool {
190224 ( self . flags & 0b00000001 ) == 1
191225 }
192226
193227 /// Change sampled flag
194228 ///
195- /// ## Examples
229+ /// # Examples
196230 ///
197231 /// ```
198232 /// use http_types::trace::TraceContext;
199233 ///
200- /// let mut context = TraceContext::new_root ();
234+ /// let mut context = TraceContext::new ();
201235 /// assert_eq!(context.sampled(), true);
202236 /// context.set_sampled(false);
203237 /// assert_eq!(context.sampled(), false);
@@ -225,8 +259,8 @@ mod test {
225259 #[ test]
226260 fn default ( ) -> crate :: Result < ( ) > {
227261 let mut headers = crate :: Headers :: new ( ) ;
228- headers. insert ( "traceparent" , "00-01-deadbeef-00" ) ;
229- let context = TraceContext :: extract ( & mut headers) ?;
262+ headers. insert ( TRACEPARENT , "00-01-deadbeef-00" ) ;
263+ let context = TraceContext :: from_headers ( & mut headers) ?. unwrap ( ) ;
230264 assert_eq ! ( context. version( ) , 0 ) ;
231265 assert_eq ! ( context. trace_id( ) , 1 ) ;
232266 assert_eq ! ( context. parent_id( ) . unwrap( ) , 3735928559 ) ;
@@ -237,8 +271,7 @@ mod test {
237271
238272 #[ test]
239273 fn no_header ( ) -> crate :: Result < ( ) > {
240- let mut headers = crate :: Headers :: new ( ) ;
241- let context = TraceContext :: extract ( & mut headers) ?;
274+ let context = TraceContext :: new ( ) ;
242275 assert_eq ! ( context. version( ) , 0 ) ;
243276 assert_eq ! ( context. parent_id( ) , None ) ;
244277 assert_eq ! ( context. flags, 1 ) ;
@@ -249,17 +282,17 @@ mod test {
249282 #[ test]
250283 fn not_sampled ( ) -> crate :: Result < ( ) > {
251284 let mut headers = crate :: Headers :: new ( ) ;
252- headers. insert ( "traceparent" , "00-01-02-00" ) ;
253- let context = TraceContext :: extract ( & mut headers) ?;
285+ headers. insert ( TRACEPARENT , "00-01-02-00" ) ;
286+ let context = TraceContext :: from_headers ( & mut headers) ?. unwrap ( ) ;
254287 assert_eq ! ( context. sampled( ) , false ) ;
255288 Ok ( ( ) )
256289 }
257290
258291 #[ test]
259292 fn sampled ( ) -> crate :: Result < ( ) > {
260293 let mut headers = crate :: Headers :: new ( ) ;
261- headers. insert ( "traceparent" , "00-01-02-01" ) ;
262- let context = TraceContext :: extract ( & mut headers) ?;
294+ headers. insert ( TRACEPARENT , "00-01-02-01" ) ;
295+ let context = TraceContext :: from_headers ( & mut headers) ?. unwrap ( ) ;
263296 assert_eq ! ( context. sampled( ) , true ) ;
264297 Ok ( ( ) )
265298 }
0 commit comments