77use std:: fs;
88use std:: iter:: FromIterator ;
99use std:: path:: Path ;
10+ use std:: str:: FromStr ;
1011use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
1112use std:: sync:: { Arc , Mutex , Weak } ;
1213use std:: time:: { Duration , SystemTime } ;
@@ -18,8 +19,8 @@ use crossbeam_channel::{Sender, unbounded};
1819use devtools_traits:: { HttpRequest as DevtoolsHttpRequest , HttpResponse as DevtoolsHttpResponse } ;
1920use headers:: {
2021 AccessControlAllowCredentials , AccessControlAllowHeaders , AccessControlAllowMethods ,
21- AccessControlAllowOrigin , AccessControlMaxAge , CacheControl , ContentLength , ContentType ,
22- Expires , HeaderMapExt , LastModified , Pragma , StrictTransportSecurity , UserAgent ,
22+ AccessControlAllowOrigin , AccessControlMaxAge , CacheControl , ContentLength , ContentType , ETag ,
23+ Expires , HeaderMapExt , IfNoneMatch , LastModified , Pragma , StrictTransportSecurity , UserAgent ,
2324} ;
2425use http:: header:: { self , HeaderMap , HeaderName , HeaderValue } ;
2526use http:: { Method , StatusCode } ;
@@ -1260,6 +1261,83 @@ fn test_fetch_async_returns_complete_response() {
12601261 assert_eq ! ( response_is_done( & fetch_response) , true ) ;
12611262}
12621263
1264+ #[ test]
1265+ fn test_response_cache_status_is_local ( ) {
1266+ static MESSAGE : & ' static [ u8 ] = b"cacheable content" ;
1267+ let handler =
1268+ move |_: HyperRequest < Incoming > ,
1269+ response : & mut HyperResponse < BoxBody < Bytes , hyper:: Error > > | {
1270+ // Mark this response as cacheable.
1271+ response
1272+ . headers_mut ( )
1273+ . typed_insert ( CacheControl :: new ( ) . with_max_age ( Duration :: from_secs ( 604800 ) ) ) ;
1274+ * response. body_mut ( ) = make_body ( MESSAGE . to_vec ( ) ) ;
1275+ } ;
1276+ let ( server, url) = make_server ( handler) ;
1277+
1278+ let request = RequestBuilder :: new ( Some ( TEST_WEBVIEW_ID ) , url. clone ( ) , Referrer :: NoReferrer )
1279+ . origin ( url. origin ( ) )
1280+ . policy_container ( Default :: default ( ) )
1281+ . build ( ) ;
1282+
1283+ // Use the same HttpCache for both fetches.
1284+ let mut context = new_fetch_context ( None , None , None ) ;
1285+
1286+ // Cold request - response should come from the server.
1287+ let initial_response = fetch_with_context ( request. clone ( ) , & mut context) ;
1288+ assert ! ( matches!( initial_response. cache_state, CacheState :: None ) ) ;
1289+
1290+ // Warm request - response should come from the cache.
1291+ let cached_response = fetch_with_context ( request. clone ( ) , & mut context) ;
1292+ assert ! ( matches!( cached_response. cache_state, CacheState :: Local ) ) ;
1293+
1294+ let _ = server. close ( ) ;
1295+ }
1296+
1297+ #[ test]
1298+ fn test_response_cache_status_is_validated ( ) {
1299+ static MESSAGE : & ' static [ u8 ] = b"cacheable content" ;
1300+ let handler =
1301+ move |request : HyperRequest < Incoming > ,
1302+ response : & mut HyperResponse < BoxBody < Bytes , hyper:: Error > > | {
1303+ // Check for the revalidation request.
1304+ if let Some ( _) = request. headers ( ) . typed_get :: < IfNoneMatch > ( ) {
1305+ * response. status_mut ( ) = StatusCode :: NOT_MODIFIED ;
1306+ return ;
1307+ }
1308+ // Mark this cacheable reponse as requiring revalidation upon refetch.
1309+ response
1310+ . headers_mut ( )
1311+ . typed_insert ( CacheControl :: new ( ) . with_no_cache ( ) ) ;
1312+ response
1313+ . headers_mut ( )
1314+ . typed_insert ( ETag :: from_str ( "\" 1234abcd\" " ) . unwrap ( ) ) ;
1315+ * response. body_mut ( ) = make_body ( MESSAGE . to_vec ( ) ) ;
1316+ } ;
1317+ let ( server, url) = make_server ( handler) ;
1318+
1319+ let request = RequestBuilder :: new ( Some ( TEST_WEBVIEW_ID ) , url. clone ( ) , Referrer :: NoReferrer )
1320+ . origin ( url. origin ( ) )
1321+ . policy_container ( Default :: default ( ) )
1322+ . build ( ) ;
1323+
1324+ // Use the same HttpCache for both fetches.
1325+ let mut context = new_fetch_context ( None , None , None ) ;
1326+
1327+ // Cold request - response should come from the server.
1328+ let initial_response = fetch_with_context ( request. clone ( ) , & mut context) ;
1329+ assert ! ( matches!( initial_response. cache_state, CacheState :: None ) ) ;
1330+
1331+ // Warm request - response should come from the cache after revalidation with server.
1332+ let revalidated_response = fetch_with_context ( request, & mut context) ;
1333+ assert ! ( matches!(
1334+ revalidated_response. cache_state,
1335+ CacheState :: Validated
1336+ ) ) ;
1337+
1338+ let _ = server. close ( ) ;
1339+ }
1340+
12631341#[ test]
12641342fn test_opaque_filtered_fetch_async_returns_complete_response ( ) {
12651343 static MESSAGE : & ' static [ u8 ] = b"" ;
0 commit comments