1
+ //! Specify origins that are allowed to see values via the Resource Timing API.
2
+ //!
3
+ //! # Specifications
4
+ //!
5
+ //! - [W3C Timing-Allow-Origin header](https://w3c.github.io/resource-timing/#sec-timing-allow-origin)
6
+ //! - [WhatWG Fetch Origin header](https://fetch.spec.whatwg.org/#origin-header)
7
+ //!
8
+ //! # Examples
9
+ //!
10
+ //! ```
11
+ //! # fn main() -> http_types::Result<()> {
12
+ //! #
13
+ //! use http_types::Response;
14
+ //! use http_types::trace::{AllowOrigin, Origin};
15
+ //!
16
+ //! let mut origins = AllowOrigin::new();
17
+ //! origins.push(Origin::Wildcard);
18
+ //!
19
+ //! let mut res = Response::new(200);
20
+ //! origins.apply(&mut res);
21
+ //!
22
+ //! let origins = AllowOrigin::from_headers(res)?.unwrap();
23
+ //! let origin = origins.iter().next().unwrap();
24
+ //! assert_eq!(origin, &Origin::Wildcard);
25
+ //! #
26
+ //! # Ok(()) }
27
+ //! ```
28
+
1
29
use crate :: headers:: { HeaderName , HeaderValue , Headers , ToHeaderValues , TIMING_ALLOW_ORIGIN } ;
2
- use crate :: Url ;
30
+ use crate :: { Status , Url } ;
31
+
32
+ use std:: fmt:: Write ;
33
+ use std:: fmt:: { self , Debug } ;
34
+ use std:: iter:: Iterator ;
3
35
use std:: option;
36
+ use std:: slice;
4
37
5
- /// `Timing-Allow-Origin` header.
6
- ///
7
- /// # Specifications
8
- ///
9
- /// - [W3C Timing-Allow-Origin header](https://w3c.github.io/resource-timing/#sec-timing-allow-origin)
10
- /// - [WhatWG Fetch Origin header](https://fetch.spec.whatwg.org/#origin-header)
11
- #[ derive( Debug , Clone , Eq , PartialEq ) ]
38
+ /// Specify origins that are allowed to see values via the Resource Timing API.
39
+ #[ derive( Clone , Eq , PartialEq ) ]
12
40
pub struct AllowOrigin {
13
41
origins : Vec < Origin > ,
14
42
}
@@ -23,15 +51,28 @@ impl AllowOrigin {
23
51
///
24
52
/// # Implementation note
25
53
///
26
- /// If a `"null"` value is found
54
+ /// A header value of `"null"` is treated the same as if no header was sent.
27
55
pub fn from_headers ( headers : impl AsRef < Headers > ) -> crate :: Result < Option < Self > > {
28
- let allow_origin = match headers. as_ref ( ) . get ( TIMING_ALLOW_ORIGIN ) {
29
- Some ( header ) => header ,
56
+ let headers = match headers. as_ref ( ) . get ( TIMING_ALLOW_ORIGIN ) {
57
+ Some ( headers ) => headers ,
30
58
None => return Ok ( None ) ,
31
59
} ;
32
60
33
- allow_origin. as_str ( ) . split ( "," ) ;
34
- todo ! ( ) ;
61
+ let mut origins = vec ! [ ] ;
62
+ for header in headers {
63
+ for origin in header. as_str ( ) . split ( "," ) {
64
+ match origin. trim_start ( ) {
65
+ "*" => origins. push ( Origin :: Wildcard ) ,
66
+ r#""null""# => continue ,
67
+ origin => {
68
+ let url = Url :: parse ( origin) . status ( 400 ) ?;
69
+ origins. push ( Origin :: Url ( url) ) ;
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ Ok ( Some ( Self { origins } ) )
35
76
}
36
77
37
78
/// Append an origin to the list of origins.
@@ -40,26 +81,154 @@ impl AllowOrigin {
40
81
}
41
82
42
83
/// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance.
43
- pub fn apply ( & self , headers : impl AsMut < Headers > ) {
44
- todo ! ( ) ;
84
+ pub fn apply ( & self , mut headers : impl AsMut < Headers > ) {
85
+ headers . as_mut ( ) . insert ( TIMING_ALLOW_ORIGIN , self . value ( ) ) ;
45
86
}
46
87
47
88
/// Get the `HeaderName`.
48
89
pub fn name ( & self ) -> HeaderName {
49
- todo ! ( ) ;
90
+ TIMING_ALLOW_ORIGIN
50
91
}
51
92
52
93
/// Get the `HeaderValue`.
53
94
pub fn value ( & self ) -> HeaderValue {
54
- todo ! ( ) ;
95
+ let mut output = String :: new ( ) ;
96
+ for ( n, origin) in self . origins . iter ( ) . enumerate ( ) {
97
+ let origin: HeaderValue = origin. clone ( ) . into ( ) ;
98
+ match n {
99
+ 0 => write ! ( output, "{}" , origin) . unwrap ( ) ,
100
+ _ => write ! ( output, ", {}" , origin) . unwrap ( ) ,
101
+ } ;
102
+ }
103
+
104
+ // SAFETY: the internal string is validated to be ASCII.
105
+ unsafe { HeaderValue :: from_bytes_unchecked ( output. into ( ) ) }
106
+ }
107
+
108
+ /// An iterator visiting all server timings.
109
+ pub fn into_iter ( self ) -> IntoIter {
110
+ IntoIter {
111
+ inner : self . origins . into_iter ( ) ,
112
+ }
113
+ }
114
+
115
+ /// An iterator visiting all server timings.
116
+ pub fn iter ( & self ) -> Iter < ' _ > {
117
+ Iter {
118
+ inner : self . origins . iter ( ) ,
119
+ }
120
+ }
121
+
122
+ /// An iterator visiting all server timings.
123
+ pub fn iter_mut ( & mut self ) -> IterMut < ' _ > {
124
+ IterMut {
125
+ inner : self . origins . iter_mut ( ) ,
126
+ }
127
+ }
128
+ }
129
+
130
+ impl IntoIterator for AllowOrigin {
131
+ type Item = Origin ;
132
+ type IntoIter = IntoIter ;
133
+
134
+ #[ inline]
135
+ fn into_iter ( self ) -> Self :: IntoIter {
136
+ self . into_iter ( )
137
+ }
138
+ }
139
+
140
+ impl < ' a > IntoIterator for & ' a AllowOrigin {
141
+ type Item = & ' a Origin ;
142
+ type IntoIter = Iter < ' a > ;
143
+
144
+ // #[inline]serv
145
+ fn into_iter ( self ) -> Self :: IntoIter {
146
+ self . iter ( )
147
+ }
148
+ }
149
+
150
+ impl < ' a > IntoIterator for & ' a mut AllowOrigin {
151
+ type Item = & ' a mut Origin ;
152
+ type IntoIter = IterMut < ' a > ;
153
+
154
+ #[ inline]
155
+ fn into_iter ( self ) -> Self :: IntoIter {
156
+ self . iter_mut ( )
157
+ }
158
+ }
159
+
160
+ /// A borrowing iterator over entries in `AllowOrigin`.
161
+ #[ derive( Debug ) ]
162
+ pub struct IntoIter {
163
+ inner : std:: vec:: IntoIter < Origin > ,
164
+ }
165
+
166
+ impl Iterator for IntoIter {
167
+ type Item = Origin ;
168
+
169
+ fn next ( & mut self ) -> Option < Self :: Item > {
170
+ self . inner . next ( )
171
+ }
172
+
173
+ #[ inline]
174
+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
175
+ self . inner . size_hint ( )
176
+ }
177
+ }
178
+
179
+ /// A lending iterator over entries in `AllowOrigin`.
180
+ #[ derive( Debug ) ]
181
+ pub struct Iter < ' a > {
182
+ inner : slice:: Iter < ' a , Origin > ,
183
+ }
184
+
185
+ impl < ' a > Iterator for Iter < ' a > {
186
+ type Item = & ' a Origin ;
187
+
188
+ fn next ( & mut self ) -> Option < Self :: Item > {
189
+ self . inner . next ( )
190
+ }
191
+
192
+ #[ inline]
193
+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
194
+ self . inner . size_hint ( )
195
+ }
196
+ }
197
+
198
+ /// A mutable iterator over entries in `AllowOrigin`.
199
+ #[ derive( Debug ) ]
200
+ pub struct IterMut < ' a > {
201
+ inner : slice:: IterMut < ' a , Origin > ,
202
+ }
203
+
204
+ impl < ' a > Iterator for IterMut < ' a > {
205
+ type Item = & ' a mut Origin ;
206
+
207
+ fn next ( & mut self ) -> Option < Self :: Item > {
208
+ self . inner . next ( )
209
+ }
210
+
211
+ #[ inline]
212
+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
213
+ self . inner . size_hint ( )
55
214
}
56
215
}
57
216
58
217
// Conversion from `AllowOrigin` -> `HeaderValue`.
59
218
impl ToHeaderValues for AllowOrigin {
60
219
type Iter = option:: IntoIter < HeaderValue > ;
61
220
fn to_header_values ( & self ) -> crate :: Result < Self :: Iter > {
62
- todo ! ( )
221
+ Ok ( self . value ( ) . to_header_values ( ) . unwrap ( ) )
222
+ }
223
+ }
224
+
225
+ impl Debug for AllowOrigin {
226
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
227
+ let mut list = f. debug_list ( ) ;
228
+ for origin in & self . origins {
229
+ list. entry ( origin) ;
230
+ }
231
+ list. finish ( )
63
232
}
64
233
}
65
234
@@ -83,18 +252,66 @@ impl From<Url> for Origin {
83
252
}
84
253
}
85
254
86
- // Conversion from `AllowOrigin` -> `HeaderValue`.
87
- impl ToHeaderValues for Origin {
88
- type Iter = option:: IntoIter < HeaderValue > ;
89
- fn to_header_values ( & self ) -> crate :: Result < Self :: Iter > {
90
- let res = unsafe {
91
- match self {
92
- Self :: Url ( url) => {
255
+ impl From < Origin > for HeaderValue {
256
+ fn from ( entry : Origin ) -> HeaderValue {
257
+ unsafe {
258
+ match entry {
259
+ Origin :: Url ( url) => {
93
260
HeaderValue :: from_bytes_unchecked ( format ! ( "{}" , url) . into_bytes ( ) )
94
261
}
95
- Self :: Wildcard => HeaderValue :: from_bytes_unchecked ( String :: from ( "*" ) . into_bytes ( ) ) ,
262
+ Origin :: Wildcard => {
263
+ HeaderValue :: from_bytes_unchecked ( String :: from ( "*" ) . into_bytes ( ) )
264
+ }
96
265
}
97
- } ;
98
- Ok ( Some ( res) . into_iter ( ) )
266
+ }
267
+ }
268
+ }
269
+
270
+ #[ cfg( test) ]
271
+ mod test {
272
+ use super :: * ;
273
+ use crate :: headers:: Headers ;
274
+
275
+ #[ test]
276
+ fn smoke ( ) -> crate :: Result < ( ) > {
277
+ let mut origins = AllowOrigin :: new ( ) ;
278
+ origins. push ( Origin :: Wildcard ) ;
279
+
280
+ let mut headers = Headers :: new ( ) ;
281
+ origins. apply ( & mut headers) ;
282
+
283
+ let origins = AllowOrigin :: from_headers ( headers) ?. unwrap ( ) ;
284
+ let origin = origins. iter ( ) . next ( ) . unwrap ( ) ;
285
+ assert_eq ! ( origin, & Origin :: Wildcard ) ;
286
+ Ok ( ( ) )
287
+ }
288
+
289
+ #[ test]
290
+ fn multi ( ) -> crate :: Result < ( ) > {
291
+ let mut origins = AllowOrigin :: new ( ) ;
292
+ origins. push ( Origin :: Wildcard ) ;
293
+ origins. push ( Origin :: Url ( Url :: parse ( "https://mozilla.org/" ) ?) ) ;
294
+
295
+ let mut headers = Headers :: new ( ) ;
296
+ origins. apply ( & mut headers) ;
297
+
298
+ let origins = AllowOrigin :: from_headers ( headers) ?. unwrap ( ) ;
299
+ let mut origins = origins. iter ( ) ;
300
+ let origin = origins. next ( ) . unwrap ( ) ;
301
+ assert ! ( matches!( origin, Origin :: Wildcard ) ) ;
302
+
303
+ let origin = origins. next ( ) . unwrap ( ) ;
304
+ let rhs = Url :: parse ( "https://mozilla.org/" ) ?;
305
+ assert_eq ! ( origin, & Origin :: Url ( rhs) ) ;
306
+ Ok ( ( ) )
307
+ }
308
+
309
+ #[ test]
310
+ fn bad_request_on_parse_error ( ) -> crate :: Result < ( ) > {
311
+ let mut headers = Headers :: new ( ) ;
312
+ headers. insert ( TIMING_ALLOW_ORIGIN , "server; <nori ate your param omnom>" ) ;
313
+ let err = AllowOrigin :: from_headers ( headers) . unwrap_err ( ) ;
314
+ assert_eq ! ( err. status( ) , 400 ) ;
315
+ Ok ( ( ) )
99
316
}
100
317
}
0 commit comments