1
- use std:: str:: FromStr ;
2
1
use std:: time:: Duration ;
3
2
4
3
use crate :: headers:: HeaderValue ;
5
- use crate :: Status ;
6
- use crate :: { ensure, format_err} ;
7
4
8
5
/// An individual `ServerTiming` entry.
9
6
//
@@ -17,11 +14,11 @@ use crate::{ensure, format_err};
17
14
// 4. metric + value + desc cache;desc="Cache Read";dur=23.2
18
15
//
19
16
// Multiple different entries per line are supported; separated with a `,`.
20
- #[ derive( Debug ) ]
17
+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
21
18
pub struct Entry {
22
- name : String ,
23
- dur : Option < Duration > ,
24
- desc : Option < String > ,
19
+ pub ( crate ) name : String ,
20
+ pub ( crate ) dur : Option < Duration > ,
21
+ pub ( crate ) desc : Option < String > ,
25
22
}
26
23
27
24
impl Entry {
@@ -76,90 +73,11 @@ impl From<Entry> for HeaderValue {
76
73
}
77
74
}
78
75
79
- impl FromStr for Entry {
80
- type Err = crate :: Error ;
81
- // Create an entry from a string. Parsing rules in ABNF are:
82
- //
83
- // ```
84
- // Server-Timing = #server-timing-metric
85
- // server-timing-metric = metric-name *( OWS ";" OWS server-timing-param )
86
- // metric-name = token
87
- // server-timing-param = server-timing-param-name OWS "=" OWS server-timing-param-value
88
- // server-timing-param-name = token
89
- // server-timing-param-value = token / quoted-string
90
- // ```
91
- //
92
- // Source: https://w3c.github.io/server-timing/#the-server-timing-header-field
93
- fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
94
- let mut parts = s. trim ( ) . split ( ';' ) ;
95
-
96
- // Get the name. This is non-optional.
97
- let name = parts
98
- . next ( )
99
- . ok_or_else ( || format_err ! ( "Server timing headers must include a name" ) ) ?
100
- . trim_end ( ) ;
101
-
102
- // We must extract these values from the k-v pairs that follow.
103
- let mut dur = None ;
104
- let mut desc = None ;
105
-
106
- for mut part in parts {
107
- ensure ! (
108
- !part. is_empty( ) ,
109
- "Server timing params cannot end with a trailing `;`"
110
- ) ;
111
-
112
- part = part. trim_start ( ) ;
113
-
114
- let mut params = part. split ( '=' ) ;
115
- let name = params
116
- . next ( )
117
- . ok_or_else ( || format_err ! ( "Server timing params must have a name" ) ) ?
118
- . trim_end ( ) ;
119
- let mut value = params
120
- . next ( )
121
- . ok_or_else ( || format_err ! ( "Server timing params must have a value" ) ) ?
122
- . trim_start ( ) ;
123
-
124
- match name {
125
- "dur" => {
126
- let millis: f64 = value. parse ( ) . status ( 400 ) . map_err ( |_| {
127
- format_err ! ( "Server timing duration params must be a valid double-precision floating-point number." )
128
- } ) ?;
129
- dur = Some ( Duration :: from_secs_f64 ( millis / 1000.0 ) ) ;
130
- }
131
- "desc" => {
132
- // Ensure quotes line up, and strip them from the resulting output
133
- if value. starts_with ( '"' ) {
134
- value = & value[ 1 ..value. len ( ) ] ;
135
- ensure ! (
136
- value. ends_with( '"' ) ,
137
- "Server timing description params must use matching quotes"
138
- ) ;
139
- value = & value[ 0 ..value. len ( ) - 1 ] ;
140
- } else {
141
- ensure ! (
142
- !value. ends_with( '"' ) ,
143
- "Server timing description params must use matching quotes"
144
- ) ;
145
- }
146
- desc = Some ( value. to_string ( ) ) ;
147
- }
148
- _ => continue ,
149
- }
150
- }
151
-
152
- Ok ( Entry {
153
- name : name. to_string ( ) ,
154
- dur,
155
- desc,
156
- } )
157
- }
158
- }
159
-
160
76
#[ cfg( test) ]
161
77
mod test {
162
78
use super :: * ;
79
+ use crate :: headers:: HeaderValue ;
80
+ use std:: time:: Duration ;
163
81
164
82
#[ test]
165
83
fn encode ( ) -> crate :: Result < ( ) > {
@@ -180,71 +98,4 @@ mod test {
180
98
assert_eq ! ( val, r#"Server; dur=1000; desc="A server timing""# ) ;
181
99
Ok ( ( ) )
182
100
}
183
-
184
- #[ test]
185
- fn decode ( ) -> crate :: Result < ( ) > {
186
- // Metric name only.
187
- assert_entry ( "Server" , "Server" , None , None ) ?;
188
- assert_entry ( "Server " , "Server" , None , None ) ?;
189
- assert_entry_err (
190
- "Server ;" ,
191
- "Server timing params cannot end with a trailing `;`" ,
192
- ) ;
193
- assert_entry_err (
194
- "Server; " ,
195
- "Server timing params cannot end with a trailing `;`" ,
196
- ) ;
197
-
198
- // Metric name + param
199
- assert_entry ( "Server; dur=1000" , "Server" , Some ( 1000 ) , None ) ?;
200
- assert_entry ( "Server; dur =1000" , "Server" , Some ( 1000 ) , None ) ?;
201
- assert_entry ( "Server; dur= 1000" , "Server" , Some ( 1000 ) , None ) ?;
202
- assert_entry ( "Server; dur = 1000" , "Server" , Some ( 1000 ) , None ) ?;
203
- assert_entry_err (
204
- "Server; dur=1000;" ,
205
- "Server timing params cannot end with a trailing `;`" ,
206
- ) ;
207
-
208
- // Metric name + desc
209
- assert_entry ( r#"DB; desc="a db""# , "DB" , None , Some ( "a db" ) ) ?;
210
- assert_entry ( r#"DB; desc ="a db""# , "DB" , None , Some ( "a db" ) ) ?;
211
- assert_entry ( r#"DB; desc= "a db""# , "DB" , None , Some ( "a db" ) ) ?;
212
- assert_entry ( r#"DB; desc = "a db""# , "DB" , None , Some ( "a db" ) ) ?;
213
- assert_entry ( r#"DB; desc=a_db"# , "DB" , None , Some ( "a_db" ) ) ?;
214
- assert_entry_err (
215
- r#"DB; desc="db"# ,
216
- "Server timing description params must use matching quotes" ,
217
- ) ;
218
- assert_entry_err (
219
- "Server; desc=a_db;" ,
220
- "Server timing params cannot end with a trailing `;`" ,
221
- ) ;
222
-
223
- // Metric name + dur + desc
224
- assert_entry (
225
- r#"Server; dur=1000; desc="a server""# ,
226
- "Server" ,
227
- Some ( 1000 ) ,
228
- Some ( "a server" ) ,
229
- ) ?;
230
- assert_entry_err (
231
- r#"Server; dur=1000; desc="a server";"# ,
232
- "Server timing params cannot end with a trailing `;`" ,
233
- ) ;
234
- Ok ( ( ) )
235
- }
236
-
237
- fn assert_entry_err ( s : & str , msg : & str ) {
238
- let err = Entry :: from_str ( s) . unwrap_err ( ) ;
239
- assert_eq ! ( format!( "{}" , err) , msg) ;
240
- }
241
-
242
- /// Assert an entry and all of its fields.
243
- fn assert_entry ( s : & str , n : & str , du : Option < u64 > , de : Option < & str > ) -> crate :: Result < ( ) > {
244
- let e = Entry :: from_str ( s) ?;
245
- assert_eq ! ( e. name( ) , n) ;
246
- assert_eq ! ( e. duration( ) , du. map( |du| Duration :: from_millis( du) ) ) ;
247
- assert_eq ! ( e. description( ) , de) ;
248
- Ok ( ( ) )
249
- }
250
101
}
0 commit comments