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+
129use 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 ;
335use std:: option;
36+ use std:: slice;
437
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 ) ]
1240pub struct AllowOrigin {
1341 origins : Vec < Origin > ,
1442}
@@ -23,15 +51,28 @@ impl AllowOrigin {
2351 ///
2452 /// # Implementation note
2553 ///
26- /// If a `"null"` value is found
54+ /// A header value of `"null"` is treated the same as if no header was sent.
2755 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 ,
3058 None => return Ok ( None ) ,
3159 } ;
3260
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 } ) )
3576 }
3677
3778 /// Append an origin to the list of origins.
@@ -40,26 +81,154 @@ impl AllowOrigin {
4081 }
4182
4283 /// 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 ( ) ) ;
4586 }
4687
4788 /// Get the `HeaderName`.
4889 pub fn name ( & self ) -> HeaderName {
49- todo ! ( ) ;
90+ TIMING_ALLOW_ORIGIN
5091 }
5192
5293 /// Get the `HeaderValue`.
5394 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 ( )
55214 }
56215}
57216
58217// Conversion from `AllowOrigin` -> `HeaderValue`.
59218impl ToHeaderValues for AllowOrigin {
60219 type Iter = option:: IntoIter < HeaderValue > ;
61220 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 ( )
63232 }
64233}
65234
@@ -83,18 +252,66 @@ impl From<Url> for Origin {
83252 }
84253}
85254
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) => {
93260 HeaderValue :: from_bytes_unchecked ( format ! ( "{}" , url) . into_bytes ( ) )
94261 }
95- Self :: Wildcard => HeaderValue :: from_bytes_unchecked ( String :: from ( "*" ) . into_bytes ( ) ) ,
262+ Origin :: Wildcard => {
263+ HeaderValue :: from_bytes_unchecked ( String :: from ( "*" ) . into_bytes ( ) )
264+ }
96265 }
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 ( ( ) )
99316 }
100317}
0 commit comments