@@ -2,7 +2,7 @@ use crate::response::CookieEvent;
2
2
use crate :: utils:: BoxFuture ;
3
3
use crate :: { Middleware , Next , Request } ;
4
4
5
- use crate :: http:: cookies:: { Cookie , CookieJar } ;
5
+ use crate :: http:: cookies:: { Cookie , CookieJar , Delta } ;
6
6
use crate :: http:: headers;
7
7
8
8
use std:: sync:: { Arc , RwLock } ;
@@ -13,19 +13,20 @@ use std::sync::{Arc, RwLock};
13
13
///
14
14
/// ```
15
15
/// # use tide::{Request, Response, StatusCode};
16
+ /// # use tide::http::cookies::Cookie;
16
17
/// let mut app = tide::Server::new();
17
18
/// app.at("/get").get(|cx: Request<()>| async move { Ok(cx.cookie("testCookie").unwrap().value().to_string()) });
18
19
/// app.at("/set").get(|_| async {
19
20
/// let mut res = Response::new(StatusCode::Ok);
20
- /// res.set_cookie(cookie:: Cookie::new("testCookie", "NewCookieValue"));
21
+ /// res.set_cookie(Cookie::new("testCookie", "NewCookieValue"));
21
22
/// Ok(res)
22
23
/// });
23
24
/// ```
24
25
#[ derive( Debug , Clone , Default ) ]
25
26
pub ( crate ) struct CookiesMiddleware ;
26
27
27
28
impl CookiesMiddleware {
28
- /// Creates a new CookiesMiddleware.
29
+ /// Creates a new ` CookiesMiddleware` .
29
30
pub fn new ( ) -> Self {
30
31
Self :: default ( )
31
32
}
@@ -38,30 +39,35 @@ impl<State: Send + Sync + 'static> Middleware<State> for CookiesMiddleware {
38
39
next : Next < ' a , State > ,
39
40
) -> BoxFuture < ' a , crate :: Result > {
40
41
Box :: pin ( async move {
41
- let cookie_jar = if let Some ( cookie_data) = ctx. local :: < CookieData > ( ) {
42
+ let cookie_jar = if let Some ( cookie_data) = ctx. ext :: < CookieData > ( ) {
42
43
cookie_data. content . clone ( )
43
44
} else {
44
- // no cookie data in local context, so we need to create it
45
45
let cookie_data = CookieData :: from_request ( & ctx) ;
46
+ // no cookie data in ext context, so we try to create it
46
47
let content = cookie_data. content . clone ( ) ;
47
- ctx = ctx. set_local ( cookie_data) ;
48
+ ctx = ctx. set_ext ( cookie_data) ;
48
49
content
49
50
} ;
50
51
51
52
let mut res = next. run ( ctx) . await ?;
52
53
54
+ // Don't do anything if there are no cookies.
55
+ if res. cookie_events . is_empty ( ) {
56
+ return Ok ( res) ;
57
+ }
58
+
59
+ let jar = & mut * cookie_jar. write ( ) . unwrap ( ) ;
60
+
53
61
// add modifications from response to original
54
62
for cookie in res. cookie_events . drain ( ..) {
55
63
match cookie {
56
- CookieEvent :: Added ( cookie) => cookie_jar. write ( ) . unwrap ( ) . add ( cookie. clone ( ) ) ,
57
- CookieEvent :: Removed ( cookie) => {
58
- cookie_jar. write ( ) . unwrap ( ) . remove ( cookie. clone ( ) )
59
- }
64
+ CookieEvent :: Added ( cookie) => jar. add ( cookie. clone ( ) ) ,
65
+ CookieEvent :: Removed ( cookie) => jar. remove ( cookie. clone ( ) ) ,
60
66
}
61
67
}
62
68
63
69
// iterate over added and removed cookies
64
- for cookie in cookie_jar . read ( ) . unwrap ( ) . delta ( ) {
70
+ for cookie in jar . delta ( ) {
65
71
let encoded_cookie = cookie. encoded ( ) . to_string ( ) ;
66
72
res = res. append_header ( headers:: SET_COOKIE , encoded_cookie) ;
67
73
}
@@ -70,25 +76,61 @@ impl<State: Send + Sync + 'static> Middleware<State> for CookiesMiddleware {
70
76
}
71
77
}
72
78
73
- #[ derive( Debug , Clone ) ]
79
+ #[ derive( Debug , Default , Clone ) ]
74
80
pub ( crate ) struct CookieData {
75
- pub ( crate ) content : Arc < RwLock < CookieJar > > ,
81
+ pub ( crate ) content : Arc < RwLock < LazyJar > > ,
82
+ }
83
+
84
+ #[ derive( Debug , Default , Clone ) ]
85
+ /// Wrapper around `CookieJar`, that initializes only when actually used.
86
+ pub ( crate ) struct LazyJar ( Option < CookieJar > ) ;
87
+
88
+ impl LazyJar {
89
+ fn add ( & mut self , cookie : Cookie < ' static > ) {
90
+ self . get_jar ( ) . add ( cookie)
91
+ }
92
+
93
+ fn remove ( & mut self , cookie : Cookie < ' static > ) {
94
+ self . get_jar ( ) . remove ( cookie)
95
+ }
96
+
97
+ fn delta ( & mut self ) -> Delta < ' _ > {
98
+ self . get_jar ( ) . delta ( )
99
+ }
100
+
101
+ pub ( crate ) fn get ( & self , name : & str ) -> Option < & Cookie < ' static > > {
102
+ if let Some ( jar) = & self . 0 {
103
+ return jar. get ( name) ;
104
+ }
105
+ None
106
+ }
107
+
108
+ fn get_jar ( & mut self ) -> & mut CookieJar {
109
+ if self . 0 . is_none ( ) {
110
+ self . 0 = Some ( CookieJar :: new ( ) ) ;
111
+ }
112
+
113
+ self . 0 . as_mut ( ) . unwrap ( )
114
+ }
76
115
}
77
116
78
117
impl CookieData {
79
118
pub ( crate ) fn from_request < S > ( req : & Request < S > ) -> Self {
80
- let mut jar = CookieJar :: new ( ) ;
81
-
82
- if let Some ( cookie_headers) = req. header ( & headers:: COOKIE ) {
119
+ let jar = if let Some ( cookie_headers) = req. header ( & headers:: COOKIE ) {
120
+ let mut jar = CookieJar :: new ( ) ;
83
121
for cookie_header in cookie_headers {
84
122
// spec says there should be only one, so this is permissive
85
- for pair in cookie_header. as_str ( ) . split ( ";" ) {
123
+ for pair in cookie_header. as_str ( ) . split ( ';' ) {
86
124
if let Ok ( cookie) = Cookie :: parse_encoded ( String :: from ( pair) ) {
87
125
jar. add_original ( cookie) ;
88
126
}
89
127
}
90
128
}
91
- }
129
+
130
+ LazyJar ( Some ( jar) )
131
+ } else {
132
+ LazyJar :: default ( )
133
+ } ;
92
134
93
135
CookieData {
94
136
content : Arc :: new ( RwLock :: new ( jar) ) ,
0 commit comments