@@ -2,7 +2,7 @@ use crate::response::CookieEvent;
22use crate :: utils:: BoxFuture ;
33use crate :: { Middleware , Next , Request } ;
44
5- use crate :: http:: cookies:: { Cookie , CookieJar } ;
5+ use crate :: http:: cookies:: { Cookie , CookieJar , Delta } ;
66use crate :: http:: headers;
77
88use std:: sync:: { Arc , RwLock } ;
@@ -13,19 +13,20 @@ use std::sync::{Arc, RwLock};
1313///
1414/// ```
1515/// # use tide::{Request, Response, StatusCode};
16+ /// # use tide::http::cookies::Cookie;
1617/// let mut app = tide::Server::new();
1718/// app.at("/get").get(|cx: Request<()>| async move { Ok(cx.cookie("testCookie").unwrap().value().to_string()) });
1819/// app.at("/set").get(|_| async {
1920/// let mut res = Response::new(StatusCode::Ok);
20- /// res.set_cookie(cookie:: Cookie::new("testCookie", "NewCookieValue"));
21+ /// res.set_cookie(Cookie::new("testCookie", "NewCookieValue"));
2122/// Ok(res)
2223/// });
2324/// ```
2425#[ derive( Debug , Clone , Default ) ]
2526pub ( crate ) struct CookiesMiddleware ;
2627
2728impl CookiesMiddleware {
28- /// Creates a new CookiesMiddleware.
29+ /// Creates a new ` CookiesMiddleware` .
2930 pub fn new ( ) -> Self {
3031 Self :: default ( )
3132 }
@@ -38,30 +39,35 @@ impl<State: Send + Sync + 'static> Middleware<State> for CookiesMiddleware {
3839 next : Next < ' a , State > ,
3940 ) -> BoxFuture < ' a , crate :: Result > {
4041 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 > ( ) {
4243 cookie_data. content . clone ( )
4344 } else {
44- // no cookie data in local context, so we need to create it
4545 let cookie_data = CookieData :: from_request ( & ctx) ;
46+ // no cookie data in ext context, so we try to create it
4647 let content = cookie_data. content . clone ( ) ;
47- ctx = ctx. set_local ( cookie_data) ;
48+ ctx = ctx. set_ext ( cookie_data) ;
4849 content
4950 } ;
5051
5152 let mut res = next. run ( ctx) . await ?;
5253
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+
5361 // add modifications from response to original
5462 for cookie in res. cookie_events . drain ( ..) {
5563 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 ( ) ) ,
6066 }
6167 }
6268
6369 // iterate over added and removed cookies
64- for cookie in cookie_jar . read ( ) . unwrap ( ) . delta ( ) {
70+ for cookie in jar . delta ( ) {
6571 let encoded_cookie = cookie. encoded ( ) . to_string ( ) ;
6672 res = res. append_header ( headers:: SET_COOKIE , encoded_cookie) ;
6773 }
@@ -70,25 +76,61 @@ impl<State: Send + Sync + 'static> Middleware<State> for CookiesMiddleware {
7076 }
7177}
7278
73- #[ derive( Debug , Clone ) ]
79+ #[ derive( Debug , Default , Clone ) ]
7480pub ( 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+ }
76115}
77116
78117impl CookieData {
79118 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 ( ) ;
83121 for cookie_header in cookie_headers {
84122 // 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 ( ';' ) {
86124 if let Ok ( cookie) = Cookie :: parse_encoded ( String :: from ( pair) ) {
87125 jar. add_original ( cookie) ;
88126 }
89127 }
90128 }
91- }
129+
130+ LazyJar ( Some ( jar) )
131+ } else {
132+ LazyJar :: default ( )
133+ } ;
92134
93135 CookieData {
94136 content : Arc :: new ( RwLock :: new ( jar) ) ,
0 commit comments